https://wiki.freepascal.org/api.php?action=feedcontributions&user=Mvampire&feedformat=atomLazarus wiki - User contributions [en]2024-03-29T14:08:15ZUser contributionsMediaWiki 1.35.6https://wiki.freepascal.org/index.php?title=Feature_Ideas/UnitTests&diff=54865Feature Ideas/UnitTests2012-01-15T13:53:59Z<p>Mvampire: /* Example: JUnit tests in Eclipse Maven Project */</p>
<hr />
<div><br><br />
<br><br />
= Feature idea - Eclipse-style unit tests. =<br />
<br />
== Main idea ==<br />
<br />
The main idea is to '''have ability to write and run Unit tests from a subpackage of main project'''. This is to avoid creating a special project specifically for UnitTests. By this way, creation and running of UnitTests will be significantly simplified.<br />
<br />
IMHO, '''Unit Tests are very important'''. I also consider, that currently UnitTests support in Lazarus is very uncomfortable. And I believe, that it's one of the most important factors, or even '''the most important which limits dissemination and distribution of Lazarus and FreePascal'''.<br />
<br />
== Introduction and references ==<br />
<br />
I decided to write my feature idea after the following discussions on the forum:<br />
<br />
[http://lazarus.freepascal.org/index.php/topic,15681.0.html]<br />
[http://lazarus.freepascal.org/index.php/topic,15733.0.html]<br />
<br />
I consider that You are familiar with concept of unit testing. You can read about Unit tests on Wikipedia:<br />
[http://en.wikipedia.org/wiki/Unit_testing Unit testing on wikipedia]<br />
<br />
If you are not familiar, please read some more articles about software testing in general (for example, [http://en.wikipedia.org/wiki/Software_testing Software Testing on Wikipedia]) and why unit tests are so important and why people need them (e.g., [http://simpleprogrammer.com/2010/10/15/the-purpose-of-unit-testing/ Purpose of unit testing]).<br />
<br />
== Example: JUnit tests in Eclipse Maven Project ==<br />
<br />
Let me show, how it is implemented in Eclipse Maven Project (using screenshots from Package Explorer in Eclipse).<br />
<br />
[[Image:EclipseMavenProjectStructure.jpg]]<br />
<br />
Here is a sample project structure of Eclipse Maven Project. There are several packages in one project. Main program code is located in src/main/java. And Unit tests are located in the same project, but as a separated package - src/test/java. Physically, it's different folders on the filesystem. <br />
<br />
[[Image:EclipseMavenRunAsJUnitTest.jpg]]<br />
<br />
So, it's very easy to create and run UnitTests for an IDE user. All the user need is:<br />
* create a new Unit Test class under src/test/java, which will test functionality of one of classes under src/main/java. Both '''main classes and Unit tests are in the same project here'''<br />
* write a test<br />
* right click on individual test or the whole test package and choose Run --> Run as JUnit test<br />
* test(s) will be runned, results and stack trace (in case of exception) will be presented.<br />
<br />
<br><br />
'''Note: Maven features - not directly relevant to the main feature idea of this page. '''<br />
<br />
''There are also 2 resource folders - src/main/resources and src/test/resources. This is a cool feature of Maven. For example, if your class uses my_property_file.txt to load properties, you can have 2 files with this filename, but with different content in main/resources and test/resources. Thus, when you will run one of classes (in Java, one project can have several runnable classes, not just 1 program unit; class could be runnable as Application if it has method main()) in the src/main, it will use property file from (will look for my_property_file.txt in) src/main/resources. But, if you run a JUnit test of the same class from src/test, the same class will load properties from (will look for my_property_file.txt in) src/test/resources.''<br />
<br />
== Conclusion ==<br />
<br />
* Unit testing is very important concept widely used by all developers.<br />
* Support for unit testing is important factor for any IDE for any programming language.<br />
* I suggest that unit tests are implemented very comfortable in Eclipse (especially, when using Maven - e.g., to have different properties while running the main program and testing).<br />
* I also believe, that if it will be possible '''for user''' (does not matter how it will be exactly implemented in Lazarus code) to create and run unit tests in the same project with the main program, it could significantly increase popularity of Lazarus. Also it will simplify work of many developers.<br />
<br />
<br><br />
<br></div>Mvampirehttps://wiki.freepascal.org/index.php?title=Feature_Ideas&diff=54864Feature Ideas2012-01-15T13:45:51Z<p>Mvampire: /* IDE */</p>
<hr />
<div>{{Feature Ideas}}<br />
<br />
'''This Page is intended to collect ideas for features that could be included in Lazarus. Putting an idea here does not mean it WILL be put into Lazarus. It's just a place to gather ideas. Think of this as more of a wishlist.'''<br />
<br />
If it is clear who is going to implement the feature, for example, because you have a patch or somebody on the mailing list volunteered to implement it, then it can be added to the [http://bugs.freepascal.org/ Lazarus Bugtracker], so it can be scheduled for a release.<br />
<br />
=IDE=<br />
Features that could be included in the IDE<br />
<br />
==An Integrated IDE==<br />
<br />
Done in 0.9.29: see [[Anchor_Docking]]<br />
<br />
There are too many windows in the IDE. The more features you use the more they pile up on your desktop and become hidden behind other windows. This is confusing for the newbie and otherwise a mess.<br />
Docking windows as found in some IDEs might seem to give flexibility however in practice it is a two dimensional solution. You accidentally undock a window and have it hanging on your mouse while you try to put it back where it was.<br />
It would be better to use conventional splitters and tab controls to put it all together into a logical layout.<br />
* Associated windows should be converted to Frames and then placed on a single form or tab page on the main form<br />
* 1. There should be a Welcome Page having new project wizards, a list of recent projects, help and resources<br />
* 2. A Development tab containing sub tabs for code and for form design<br />
:* a. Code tab including Code Explorer, Source Editor and Project Manager<br />
:* b. Form design tab including Object Inspector, Form designer and Component Palette<br />
* 3 Debug tab containing the Debug windows as Frames.<br />
<br />
==Easier Dragging & Dropping==<br />
pool: after you add a component at Form, what is your next action? Editting Name/Caption in OI.<br />
Would be nice having OI focused and a key field like Name/Caption/Text selected. It would be very intuitive just having to type after dropping the component.<br />
<br />
Done in 0.9.29: Options / Form editor / Ask name on create<br />
<br />
==Common==<br />
* Open files in the same folder as the current tab editor, like SCite does, it's a bit annoying to navigate from folder to folder every time you need open a file related to the one we are editing at the moment.<br />
* enable menu access through access keys like under windows (i hate mouse sessions :o)<br />
example: <alt>-<f> for file menu<br />
<br />
==Source Editor==<br />
* (Done in 0.9.27) <s>When focusing/selecting one BEGIN the respective END should be in RED to show where it really ends.</s><br />
<br />
* Multiple splitting (and close the split) the editor to horizontal and vertical views like with VIM, so I can do split for example Vertically, and inside do a split for Horizontal editor. And we can close each split by right click on the movable splitter and select it from a menu item.<br />
<br />
* VIM keyboard support - require implementation of a command line like that can accept keyboards like with the VIM text editor.<br />
<br />
* (Done in 0.9.29) <s> Movable tabs, so we can place tabs as we wish instead of keeping them where they where opened. </s><br />
<br />
* (Done in 0.9.27) <s> Ability to have a way to fold code arbitrary by using comments with special chars such as </s><br />
some code {% /* } <-- start folding <br />
more code<br />
...<br />
...<br />
and more code {% */ } <-- stop folding <br />
<br />
* Multi linguistic spell checkers for strings and comments, using aspell, hspell, ispell etc... (aspell and hspell already have binding for FPC)<br />
<br />
* Color code on errors with special underline or some type of color that the user can select/change that will help to locate where the problem is (not instead of the error message that exists today).<br />
<br />
* Ability to keep the string mark (that added to 0.9.27) of a word/selected string as permanent with a toggle of a menu item/key/toolbar or something<br />
: QUESTION: Does that refer to the "highlight all occurrences" of "word under caret"/"current selection"?<br />
: That is possible (try alt-M)<br />
<br />
* Inline help for a command/reserve word that the mouse cursor/keyboard caret are under that word (not instead of the tooltip that exists today when the mouse is hover the command/variable).<br />
<br />
* (Done) <s>When pressing on CTRL on a file/variable/type etc.. that we are trying to load, it will place a tooltip telling what is going to be opened (the file + the line number)</s> (The information is shown when hovering over the variable)<br />
<br />
* <s>Ability to save and load color scheme for syntax highlight.</s> Doen in 0,9,29<br />
<br />
* Within the "Edit" menu, add an option called "Replace in files...", which allow find and replace text in all the units of the application, doing it at once.<br />
<br />
===Improvement of the menu and toolbars===<br />
* to allow final-user customization<br />
* to obtain more styled menu (similar as Shagrouni xp-menu style)<br />
<br />
==Integrated and Working Debugger==<br />
Debugging from inside the IDE can be a frustrating process. I believe this is a combination of GDB being more C/C++ centric, or because it is a more generalized standalone debugger. Either way, I think the ultimate solution is to write a custom debugger for use with FPC and Lazarus IDE. This way it will be possible for the debugger to support ALL Object Pascal and Lazarus IDE requirements, or maybe even be compiled into the Lazarus IDE itself. GDB does support Object Pascal, but not nearly at the level it does with C/C++.<br />
<br />
An IDE without sufficient debugging support is a hard sell to would-be contributors or Delphi converts. This might also have contributed to some of the negative comments received in the Delphi non-technical newsgroup. Make no mistake, I love the many unique IDE features Lazarus has to offer - it beats Delphi IDE by far.<br />
<br />
Currently I know of two custom written debuggers, implemented in Object Pascal:<br />
<br />
* '''fpdebug''': Located in the <laz>/debugger/fpdebug/ directory. Unfortunately at the time of this writting, that project is Windows only.<br />
* '''Duby''': A newly started project available on SourceForge.net at [http://sourceforge.net/projects/duby/ this] link.<br />
<br />
Please try out these projects and contribute if you can.<br />
<br />
--[[User:Ggeldenhuys|Ggeldenhuys]] 15:18, 2 October 2009 (CEST)<br />
<br />
==Object Inspector==<br />
<br />
===Search field===<br />
add a "search" text box at the top or bottom (preferred, maybe user settable) of Object Inspector to be able to search for a name or value (maybe with right click menu checkable options on whether Name or Value or both are searched) in the properties/events list<br />
<br />
Question: What about sub properties? <br />
<br />
===Recent page===<br />
add a "Recent" tab to Object Inspector apart from the "Favorites" one, would be more useful than the Favorites one since it's automatic (would keep all properties/events that have been edited [fields that have either been changed or ENTER was pressed to CONFIRM/REset their current value - focus lost events shouldn't be considered editing if value of property/event field didn't change])<br />
<br />
Question: How are items removed from the list?<br />
<br />
<br />
===Not registered component classes===<br />
Add a field in the object inspector to allow specify the real class that will be instantiated, with that will allow to uses derived classes without remake/link the IDE, of course the new fields for the real class will not be available to edit in designer.<br />
An example:<br />
We place a TEdit in a form, but we want to use a derived class we made that add a new field/property/method to TEdit and we will use that new field/property/method programming but in design time it's enough to work with TEdit, actually there is an option to change the class but only to another one that's already linked on the IDE.<br />
<br />
Note: The class must be registered. This is needed for streaming and RTTI. If you don't want to register the real class, then register a fake class. It would be possible to create an artificial derived class from a registered component class on the fly. This has nothing to do with the OI. A dialog to manage all artificial classes must be implemented and a menu item must be added somewhere.<br />
<br />
==Refactoring==<br />
===Global Variable/Method renaming===<br />
<s>Proper renaming (as in if I rename a field in an object it can change everything that uses it but NOT other stuff that happens to have the same name) -Plugwash</s><br />
: Already implemented: See Source Editor -> popup menu -> refactoring -> rename identifier<br />
<br />
===Parameter Renaming in Methods===<br />
adding a parameter to a method (and updating everything that uses it) -Plugwash<br />
: How should that work? What should the IDE/codetools put as parameter at the places?<br />
:: You specify that when you ask for the parameter to be added. [[User:Plugwash|Plugwash]] 05:09, 30 September 2006 (CEST)<br />
: For example, you add a parameter "b: boolean". The method is used a 100 times.<br />
* Possibility 1: You add a default parameter too. No change to the using methods is needed.<br />
* Possibility 2: You press compile and the compiler will jump to every occurence and you can decide, what to do.<br />
* Possibility 3: A new dialog is added to the IDE, where you can specify to insert a ",false" at every occurence.<br />
* Possibility 4: ?<br />
Do you have 3 in mind or 4?<br />
<br />
===Parameter reordering===<br />
like Delphi. A dialog to add, remove, reorder parameters of a function, method, property.<br />
<br />
===Encapsulating Fields===<br />
though thats not really needed in object pascal because object pascal has properties but its vital in cleaning up thrown together java. -Plugwash<br />
: Please give an example.<br />
<br />
===Improvement of the IDE like GExperts does for Delphi===<br />
* Backup tool...<br />
* Tab : provision to set Tab spacing<br />
* Rename Components (where it is possible too, to fill some properties in this moment. f.e. Button: Caption, Width, Height, Action)<br />
<br />
== I have a dream of components : Jedi ==<br />
Sure I switch immediatly from Delphi to Lazarus.<br />
<br />
* The Jedi components are a mix of OS dependant, Borland dependant and architecture specific code. I doubt these will ever work on anything but windows/x86. (and maybe win64 with a bit shoehorning). Example: To use a string routine in the strings unit of JCL, you have to fix like 12 units, some of which poke around in the Borland specific debug code. And then you haven't even reached the units of the JVCL, besides this there is a [[licensing]] problem. [[User:Marcov|Marcov]] 18:27, 4 October 2008 (CEST)<br />
<br />
==Code completion==<br />
===Create/Free objects===<br />
Declare an object myObj:TMyClass into a TForm class an type some Ctrl-Shift-Key, then the IDE adds a line myObj:=TMyClass.Create in FormCreate and myObj.Free in FormDestroy. -Rednaxel<br />
<br />
===Add list properties===<br />
Declaring an indexed property like 'property Items[i: integer]: TAType' and using this feature (Ctrl-Shift-Key, Menu), should popup a dialog to setup some parameters and then it should add a private variable fItems: TFPList; (or 'array of' or TList or ^AType), create code in the constructor, destroy code in the destructor and access methods GetItems, SetItems.<br />
<br />
==Visual Form Inheritance==<br />
VFI (Visual From Inheritance) works in Lazarus but there are some missing features:<br />
*When ancestor and descendant are open in the IDE and a property of the ancestor is changed, then change the descendant too.<br />
<br />
==Conditional Compiler Options==<br />
<br />
See [[Conditional Compiler Options]].<br />
<br />
==Help==<br />
===A small tutorial for Object Oriented Programming===<br />
I wish to have in Lazarus as in Eclipse in the Help menu a Tutorial for Object Oriented Programming.<br />
* This is a good idea, but I think a '''offline''' help browser is first required. Lucky for you, I already implemented such a beast, called [http://sourceforge.net/projects/fpgui/files/fpGUI/0.8/ DocView]. :-) I also have a few old programming manuals around that could contribute to the contents. I know Borland released the OOP chapter what was included in the Turbo Pascal v5.5 & v6 manuals for everybody to use (freely downloadable). So maybe that will be the best place to start. A downloadable copy of the OOP Chapter can be found [http://opensoft.homeip.net:8080/articles/ here]. --[[User:Ggeldenhuys|Ggeldenhuys]] 15:03, 14 January 2011 (CET)<br />
<br />
==Better Macro support by making them easier to find==<br />
See the feature request [http://bugs.freepascal.org/view.php?id=14558 #14558] in Mantis. In summary, add a button next to EditBox controls which support macro usage. Clicking the button will display a popup menu with available macros at that level and insert the selected macro into the EditBox at the cursor position.<br />
<br />
==Copy and Paste Support for a lazy programmer (in properties-editor) ==<br />
Maybe it's by design (because of the same behaviour under win and mac)... but I think, it's a little bit of annoying. When I try to copy the chosen "name" of a component out of proterties-editor by selecting it and ctrl+c, I will not get the text of the name, but the object/component description itself (which is useless, I guess). There is not even a "copy" item in the context-menu. I use very long descriptive names for components (and variables) and it would be nice, to get these unhandy names somehow into the clipboard. [[User:KaLi|KaLi]] 14:56, 30 June 2010 (CEST)<br />
: This seems to work in 0.9.31, both Ctrl+C and via context-menu [http://wiki.lazarus.freepascal.org/User:Bart Bart]<br />
<br />
==Code Library Window or Tab ==<br />
It would be great to have a built in location to store all those useful pieces of code. Ones that you have created, or found useful.<br />
<br />
My ideal feature list would be:<br />
* Save and Load my personal code<br />
* Attach Zip file ( useful for keeping a sample project, showing the code in use).<br />
* Ability to Upload Code / zip to central online repository.<br />
* Ability to synchronize or select repository code and download to my own code store.<br />
* Code could be sectioned into well thought out categories, like Types of controls, Libraries, complete project examples, Tutorials, etc.<br />
* A place to show the most recent code submitted by Category.<br />
<br />
It would also be a good place to help people who need some assistance. I would have the following options:<br />
* Option to Ask for help on a control or subject matter.<br />
* Notification when a solution is uploaded to the repository.<br />
* Maybe a points system rating the assistance given out of 10 ( 1 being not useful, 10 being just what I needed)<br />
<br />
== Write and run unit tests from subpackage of project being tested ==<br />
The main idea is to have ability to write and run Unit tests from a subpackage of main project. '''Please see [[Feature_Ideas/UnitTests]]'''<br />
<br />
=LCL=<br />
<br />
==Components==<br />
<br />
Components that are possible to make that will work across all platforms supported by Lazarus<br />
* Do you mean customdrawn components? If so, that is already possible. I think the TStringGrid is a customdrawn component. Alternatively, look at [[fpGUI]] for a completely customdrawn GUI toolkit. --[[User:Ggeldenhuys|Ggeldenhuys]] 23:56, 30 September 2009 (CEST)<br />
<br />
===Visual===<br />
*Already implemented: Help system for apps created by Laz! It is a plugin system. That's why it supports cross-platform help system and using the OS's native help system.<br />
<br />
*The current LCL TStringGrid is a nice improvement over Delphi's feeble control with the same name. Would be great if it could be improved even more!<br />
**<s>Support for data validation event handler, similar to Orpheus grid's OnUserValidation event.</s> ''There are many events, for example OnEditingDone, and OnValidateEntry to do this. See [[Grids Reference Page]]''<br />
**Support for retrieving, displaying, editing and storing data that are stored in an existing memory structure or in a file rather than in grid itself, similar to the Orpheus grid's OnGetCellData and OnGetCellAttributes events. ''There is the TTIGrid, that can show TCollection, TList and TFPList of TPersistent. What else do you need? Additionally, you can bind a TBufDataset to a dbgrid...''<br />
**An TImage Component wich support transparent winxp alpha 32 bit icon images<br />
**Support for RightToLeft BidiMode.<br />
*TTrayIcon<br />
** Add a TImageList property in TTrayIcon to change TrayIcon icon or animate it<br />
<br />
===NonVisual===<br />
*Clipboard and TTimer that don't depend on LCL. If you use these in a library or console app, the compiler drags in a lot of unused and unneeded LCL code. If you have multiple DLL's, this code is duplicated in each. Current implementations appear to follow the Delphi design approach, where Clipboard and TTimer have dependencies on the VCL - this probably wasn't necessary.<br />
** TfpTimer is a non-LCL timer already available in FPC in unit '''fptimer.pp'''. Online help is available [http://lazarus-ccr.sourceforge.net/docs/fcl/fptimer/index.html here]. --[[User:Ggeldenhuys|Ggeldenhuys]] 14:38, 2 September 2009 (CEST)<br />
** This may be true on Windows, but it´s False for Linux. I recently implemented TTimer for qt interface and the implementation calls Qt API. I think the same applies to Clipboard, since the clipboard depends on the widgetset used too (gtk or qt for example), not only on the operating system. --[[User:Sekelsenmat|Sekelsenmat]] 20:40, 5 Mar 2006 (CET)<br />
** The LCL is a visual component library. None visual components are provided by the FCL.<br />
** Lazarus will only provide components based on the LCL.<br />
*** Well, that's not entirely true. LCL bases a lot of components on FCL classes. TCustomCanvas, TAction etc. :-) --[[User:Ggeldenhuys|Ggeldenhuys]] 17:17, 4 October 2008 (CEST)<br />
<br />
==PDA Support==<br />
<br />
Something that currently does not exist is a complete multiplatform solution for PDAs. If we can implement this, Lazarus will become the first multiplatform RAD for PDAs ever created. Some devices work with Java, some work with c++ and Qtopia, some work with their particular APIs, there is no single solution currently that can embrace a large portion of PDAs. Application have to be redone for every PDA or simply just run on one of them (what most people do).<br />
<br />
The LCL implements many things. It is not necessary to implement them all to have a reasonably working Lazarus PDA widgetset. The most important things to be implementeded for PDAs are:<br />
<br />
* TApplication<br />
* TForm<br />
* TScreen<br />
* TCanvas and painting<br />
* Most Events (onClick and OnPaint at least)<br />
* Basic widgets like: TButton, TLabel, TEdit, TMemo.<br />
* Only minimum nessary LCLIntf functions to make TCanvas work. (GetDC, ReleaseDC, BeginPaint, EndPaint and some painting functions)<br />
<br />
Others can be easely implemented by people who need them after those are ready.<br />
<br />
Here is a list of some PDAs and the status of their support on Free Pascal / Lazarus:<br />
<br />
* Windows CE - The compiler already works really well cross compiling for Windows CE. The [[Windows CE Interface]] is under implementation.<br />
* Symbian OS - Possible for their ARM devices. Requires first adding a new Runtime Library for this OS to the Compiler. This OS has it's own API. There are open source projects for python and c to work on Symbian OS, so Free Pascal should work as well when a RTL is written.<br />
* Qtopia - For Zaurus and other linux-based PDAs. This is much easier now that basic Qt4 widgetset has being implemented, because Qtopia is very similar to Qt4. Some magick on the bindings is expected to make the same widgetset code to run for both Qtopia and Qt4.<br />
* Java - Some handheld devices, specially phones, only support java bytecode. May seam a little odd, but there is a Pascal Compiler capable of compiling the source into a jar and then into bytecode. It's name is Midlet Pascal http://www.midletpascal.com/ It may be possible to develop a similar technology that adds a java bytecode target for Free Pascal. Some people already start porting midletpascal to delphi http://forum.boolean.name/attachment.php?attachmentid=7975&d=1255392966, follow instructions on http://www.pascalgamedevelopment.com/forum/index.php?topic=5466.0 to access it.<br />
* Palm OS - PalmOS 5 Garnet support requires that first the 68k compiler is working (runs on ARM processor with a kind of emulation) and then a RTL for palmos needs to be written. Only Free Pascal 1.0 supports 68k, this wasn't updated and the new compiler 2.0 doesn't support it yet (work in progress by [[fpc:User:Chain-Q]]), so will be a problem.<br />
<br />
PalmOS 6 Cobalt is fully arm, so it only requires a rtl, but it has zero devices working on it currently and won't be on the market until 2007 at least.<br />
<br />
Other compilers that support this target: [http://www.ppcompiler.org Pascal & Palm Compiler] and [http://hspascal.fihl.net/ HsPascal]. Unfortunately those compilers are not open source, so we can't use their code, we need to implement PalmOS RTL from zero.<br />
<br />
==More Interfaces==<br />
* Cocoa - Requires that first OpenSTEP bindings are created for Free Pascal.<br />
* BeAPI - interface for Haiku, ZetaOS and BeOS. There is no powerful RAD on this OS.<br />
** Bindings to the BeAPI has been started - though not part of the Lazarus project. Have a look at SourceForge [http://befpc.sourceforge.net/ befpc] project.<br />
* [http://www.libsdl.org SDL] - something like what was done in [http://www.kanzelsberger.com/pixel/ Pixel Image Editor].<br />
* MacApp 2.0 - For 68k-based Mac computers. MacApp was Apple Computer's primary object oriented application framework for the Mac OS for much of the 1990s. First released in 1985, it is arguably the first such system to be widely used, notably on a microcomputer platform. Microsoft's MFC and Borland's OWL were both based directly on MacApp concepts. This framework was completely written on Object Pascal, so it should integrate really nicely with Free Pascal and Lazarus. When Apple moved to PowerPC it also dropped Object Pascal support, but that's another story. A emulator for 68k-Mac can be easely set up with [http://basilisk.cebix.net/ BasiliskII]. There is also a project called MacApp2PPC that upgraded MacApp pascal code to run on Power PCs. <br />
* OS/2 native - Free Pascal works well with OS/2, so this should be quite possible.<br />
* Solaris - OpenSolaris versions exist and it's quite similar to Linux so why not ?<br />
* Web Interface - Application compiles to an embedded HTTP Server who serves forms in ajax pages (or compiles it to an Apache Module), like Intraweb in Delphi. Discussion in http://www.lazarus.freepascal.org/index.php?name=PNphpBB2&file=viewtopic&t=2489<br />
* [http://opensoft.homeip.net/fpgui/ fpGUI] - A widget set written in Free Pascal without any large library requirements. fpGUI talks directly to the underlying graphics library and is very easy to port to new platforms. Currently it supports Linux, ARM-Linux, FreeBSD, Windows and WinCE. - Under development here: [[fpGUI_Interface]]<br />
<br />
=== improved EventQueue ===<br />
* [[Lazarus improved Event Queue]]<br />
<br />
=== Lua ===<br />
* Support for Lua interfacing. (Binary modules?) <br />
** What is Lua interfacing? Is it a graphical widgetset? What is the difference between what you want and is supplied by the lua units of fpc: http://svn.freepascal.org/svn/fpc/trunk/packages/extra/lua/<br />
<br />
=Fixes branch releases=<br />
Currently we only have snapshots of the fixes branch, and it would be nice to also have them as releases. To have releases, those builds would have to be tested by someone, so I volunteer for that. What is needed is a testing checklist, and the start of it can be found on my page, [[User:Arny]]. Any further ideas are welcome!<br />
<br />
* This is already done and maintained for v0.9.28 onwards of Lazarus. Downloads of ''fixes'' releases are available on SourceForge or at [[git_mirrors|GitHub]]. --[[User:Ggeldenhuys|Ggeldenhuys]] 16:03, 16 November 2009 (CET)<br />
<br />
[[Category:Lazarus]]<br />
[[Category:Proposals]]</div>Mvampirehttps://wiki.freepascal.org/index.php?title=Feature_Ideas/UnitTests&diff=54863Feature Ideas/UnitTests2012-01-15T13:39:59Z<p>Mvampire: /* Feature idea - Eclipse-style unit tests. */</p>
<hr />
<div><br><br />
<br><br />
= Feature idea - Eclipse-style unit tests. =<br />
<br />
== Main idea ==<br />
<br />
The main idea is to '''have ability to write and run Unit tests from a subpackage of main project'''. This is to avoid creating a special project specifically for UnitTests. By this way, creation and running of UnitTests will be significantly simplified.<br />
<br />
IMHO, '''Unit Tests are very important'''. I also consider, that currently UnitTests support in Lazarus is very uncomfortable. And I believe, that it's one of the most important factors, or even '''the most important which limits dissemination and distribution of Lazarus and FreePascal'''.<br />
<br />
== Introduction and references ==<br />
<br />
I decided to write my feature idea after the following discussions on the forum:<br />
<br />
[http://lazarus.freepascal.org/index.php/topic,15681.0.html]<br />
[http://lazarus.freepascal.org/index.php/topic,15733.0.html]<br />
<br />
I consider that You are familiar with concept of unit testing. You can read about Unit tests on Wikipedia:<br />
[http://en.wikipedia.org/wiki/Unit_testing Unit testing on wikipedia]<br />
<br />
If you are not familiar, please read some more articles about software testing in general (for example, [http://en.wikipedia.org/wiki/Software_testing Software Testing on Wikipedia]) and why unit tests are so important and why people need them (e.g., [http://simpleprogrammer.com/2010/10/15/the-purpose-of-unit-testing/ Purpose of unit testing]).<br />
<br />
== Example: JUnit tests in Eclipse Maven Project ==<br />
<br />
Let me show, how it is implemented in Eclipse Maven Project (using screenshots from Package Explorer in Eclipse).<br />
<br />
[[Image:EclipseMavenProjectStructure.jpg]]<br />
<br />
Here is a sample project structure of Eclipse Maven Project. There are several packages in one project. Main program code is located in src/main/java. And Unit tests are located in the same project, but as a separated package - src/test/java. Physically, it's different folders on the filesystem. <br />
<br />
[[Image:EclipseMavenRunAsJUnitTest.jpg]]<br />
<br />
So, it's very easy to create and UnitTests for an IDE user. All the user need is:<br />
* create a new Unit Test class under src/test/java, which will test functionality of one of classes under src/main/java. Both '''main classes and Unit tests are in the same project here'''<br />
* write a test<br />
* right click on individual test or the whole test package and choose Run --> Run as JUnit test<br />
* test(s) will be runned, results and stack trace (in case of exception) will be presented.<br />
<br />
<br><br />
'''Note: Maven features - not directly relevant to the main feature idea of this page. '''<br />
<br />
''There are also 2 resource folders - src/main/resources and src/test/resources. This is a cool feature of Maven. For example, if your class uses my_property_file.txt to load properties, you can have 2 files with this filename, but with different content in main/resources and test/resources. Thus, when you will run one of classes (in Java, one project can have several runnable classes, not just 1 program unit; class could be runnable as Application if it has method main()) in the src/main, it will use property file from (will look for my_property_file.txt in) src/main/resources. But, if you run a JUnit test of the same class from src/test, the same class will load properties from (will look for my_property_file.txt in) src/test/resources.''<br />
<br />
== Conclusion ==<br />
<br />
* Unit testing is very important concept widely used by all developers.<br />
* Support for unit testing is important factor for any IDE for any programming language.<br />
* I suggest that unit tests are implemented very comfortable in Eclipse (especially, when using Maven - e.g., to have different properties while running the main program and testing).<br />
* I also believe, that if it will be possible '''for user''' (does not matter how it will be exactly implemented in Lazarus code) to create and run unit tests in the same project with the main program, it could significantly increase popularity of Lazarus. Also it will simplify work of many developers.<br />
<br />
<br><br />
<br></div>Mvampirehttps://wiki.freepascal.org/index.php?title=Feature_Ideas/UnitTests&diff=54862Feature Ideas/UnitTests2012-01-15T13:39:31Z<p>Mvampire: /* Conclusion */</p>
<hr />
<div>= Feature idea - Eclipse-style unit tests. =<br />
<br />
== Main idea ==<br />
<br />
The main idea is to '''have ability to write and run Unit tests from a subpackage of main project'''. This is to avoid creating a special project specifically for UnitTests. By this way, creation and running of UnitTests will be significantly simplified.<br />
<br />
IMHO, '''Unit Tests are very important'''. I also consider, that currently UnitTests support in Lazarus is very uncomfortable. And I believe, that it's one of the most important factors, or even '''the most important which limits dissemination and distribution of Lazarus and FreePascal'''.<br />
<br />
== Introduction and references ==<br />
<br />
I decided to write my feature idea after the following discussions on the forum:<br />
<br />
[http://lazarus.freepascal.org/index.php/topic,15681.0.html]<br />
[http://lazarus.freepascal.org/index.php/topic,15733.0.html]<br />
<br />
I consider that You are familiar with concept of unit testing. You can read about Unit tests on Wikipedia:<br />
[http://en.wikipedia.org/wiki/Unit_testing Unit testing on wikipedia]<br />
<br />
If you are not familiar, please read some more articles about software testing in general (for example, [http://en.wikipedia.org/wiki/Software_testing Software Testing on Wikipedia]) and why unit tests are so important and why people need them (e.g., [http://simpleprogrammer.com/2010/10/15/the-purpose-of-unit-testing/ Purpose of unit testing]).<br />
<br />
== Example: JUnit tests in Eclipse Maven Project ==<br />
<br />
Let me show, how it is implemented in Eclipse Maven Project (using screenshots from Package Explorer in Eclipse).<br />
<br />
[[Image:EclipseMavenProjectStructure.jpg]]<br />
<br />
Here is a sample project structure of Eclipse Maven Project. There are several packages in one project. Main program code is located in src/main/java. And Unit tests are located in the same project, but as a separated package - src/test/java. Physically, it's different folders on the filesystem. <br />
<br />
[[Image:EclipseMavenRunAsJUnitTest.jpg]]<br />
<br />
So, it's very easy to create and UnitTests for an IDE user. All the user need is:<br />
* create a new Unit Test class under src/test/java, which will test functionality of one of classes under src/main/java. Both '''main classes and Unit tests are in the same project here'''<br />
* write a test<br />
* right click on individual test or the whole test package and choose Run --> Run as JUnit test<br />
* test(s) will be runned, results and stack trace (in case of exception) will be presented.<br />
<br />
<br><br />
'''Note: Maven features - not directly relevant to the main feature idea of this page. '''<br />
<br />
''There are also 2 resource folders - src/main/resources and src/test/resources. This is a cool feature of Maven. For example, if your class uses my_property_file.txt to load properties, you can have 2 files with this filename, but with different content in main/resources and test/resources. Thus, when you will run one of classes (in Java, one project can have several runnable classes, not just 1 program unit; class could be runnable as Application if it has method main()) in the src/main, it will use property file from (will look for my_property_file.txt in) src/main/resources. But, if you run a JUnit test of the same class from src/test, the same class will load properties from (will look for my_property_file.txt in) src/test/resources.''<br />
<br />
== Conclusion ==<br />
<br />
* Unit testing is very important concept widely used by all developers.<br />
* Support for unit testing is important factor for any IDE for any programming language.<br />
* I suggest that unit tests are implemented very comfortable in Eclipse (especially, when using Maven - e.g., to have different properties while running the main program and testing).<br />
* I also believe, that if it will be possible '''for user''' (does not matter how it will be exactly implemented in Lazarus code) to create and run unit tests in the same project with the main program, it could significantly increase popularity of Lazarus. Also it will simplify work of many developers.<br />
<br />
<br><br />
<br></div>Mvampirehttps://wiki.freepascal.org/index.php?title=Feature_Ideas/UnitTests&diff=54861Feature Ideas/UnitTests2012-01-15T13:38:27Z<p>Mvampire: Feature idea - Eclipse-style unit tests. The main idea is to have ability to run Unit tests from a subpackage of main project.</p>
<hr />
<div>= Feature idea - Eclipse-style unit tests. =<br />
<br />
== Main idea ==<br />
<br />
The main idea is to '''have ability to write and run Unit tests from a subpackage of main project'''. This is to avoid creating a special project specifically for UnitTests. By this way, creation and running of UnitTests will be significantly simplified.<br />
<br />
IMHO, '''Unit Tests are very important'''. I also consider, that currently UnitTests support in Lazarus is very uncomfortable. And I believe, that it's one of the most important factors, or even '''the most important which limits dissemination and distribution of Lazarus and FreePascal'''.<br />
<br />
== Introduction and references ==<br />
<br />
I decided to write my feature idea after the following discussions on the forum:<br />
<br />
[http://lazarus.freepascal.org/index.php/topic,15681.0.html]<br />
[http://lazarus.freepascal.org/index.php/topic,15733.0.html]<br />
<br />
I consider that You are familiar with concept of unit testing. You can read about Unit tests on Wikipedia:<br />
[http://en.wikipedia.org/wiki/Unit_testing Unit testing on wikipedia]<br />
<br />
If you are not familiar, please read some more articles about software testing in general (for example, [http://en.wikipedia.org/wiki/Software_testing Software Testing on Wikipedia]) and why unit tests are so important and why people need them (e.g., [http://simpleprogrammer.com/2010/10/15/the-purpose-of-unit-testing/ Purpose of unit testing]).<br />
<br />
== Example: JUnit tests in Eclipse Maven Project ==<br />
<br />
Let me show, how it is implemented in Eclipse Maven Project (using screenshots from Package Explorer in Eclipse).<br />
<br />
[[Image:EclipseMavenProjectStructure.jpg]]<br />
<br />
Here is a sample project structure of Eclipse Maven Project. There are several packages in one project. Main program code is located in src/main/java. And Unit tests are located in the same project, but as a separated package - src/test/java. Physically, it's different folders on the filesystem. <br />
<br />
[[Image:EclipseMavenRunAsJUnitTest.jpg]]<br />
<br />
So, it's very easy to create and UnitTests for an IDE user. All the user need is:<br />
* create a new Unit Test class under src/test/java, which will test functionality of one of classes under src/main/java. Both '''main classes and Unit tests are in the same project here'''<br />
* write a test<br />
* right click on individual test or the whole test package and choose Run --> Run as JUnit test<br />
* test(s) will be runned, results and stack trace (in case of exception) will be presented.<br />
<br />
<br><br />
'''Note: Maven features - not directly relevant to the main feature idea of this page. '''<br />
<br />
''There are also 2 resource folders - src/main/resources and src/test/resources. This is a cool feature of Maven. For example, if your class uses my_property_file.txt to load properties, you can have 2 files with this filename, but with different content in main/resources and test/resources. Thus, when you will run one of classes (in Java, one project can have several runnable classes, not just 1 program unit; class could be runnable as Application if it has method main()) in the src/main, it will use property file from (will look for my_property_file.txt in) src/main/resources. But, if you run a JUnit test of the same class from src/test, the same class will load properties from (will look for my_property_file.txt in) src/test/resources.''<br />
<br />
== Conclusion ==<br />
<br />
* Unit testing is very important concept widely used by all developers.<br />
* Support for unit testing is important factor for any IDE for any programming language.<br />
* I suggest that unit tests are implemented very comfortable in Eclipse (especially, when using Maven - e.g., to have different properties while running the main program and testing).<br />
* I also believe, that if it will be possible '''for user''' (does not matter how it will be exactly implemented in Lazarus code) to create and run unit tests in the same project with the main program, it could significantly increase popularity of Lazarus. Also it will simplify work of many developers.</div>Mvampirehttps://wiki.freepascal.org/index.php?title=File:EclipseMavenRunAsJUnitTest.jpg&diff=54860File:EclipseMavenRunAsJUnitTest.jpg2012-01-15T13:03:55Z<p>Mvampire: running a JUnit tests from Eclipse Maven Project</p>
<hr />
<div>running a JUnit tests from Eclipse Maven Project</div>Mvampirehttps://wiki.freepascal.org/index.php?title=File:EclipseMavenProjectStructure.jpg&diff=54859File:EclipseMavenProjectStructure.jpg2012-01-15T13:02:37Z<p>Mvampire: Eclipse Maven Project Structure</p>
<hr />
<div>Eclipse Maven Project Structure</div>Mvampirehttps://wiki.freepascal.org/index.php?title=Multithreaded_Application_Tutorial&diff=45251Multithreaded Application Tutorial2010-09-24T12:09:06Z<p>Mvampire: /* Do you need multi-threading? */</p>
<hr />
<div>{{Multithreaded Application Tutorial}}<br />
<br />
== Overview ==<br />
This page will try to explain how to write and debug a multi-threaded application with Free Pascal and Lazarus. A multi-threaded application is one that creates two or more threads of execution that work at the same time. If you are new to multi-threading, please read the paragraph "Do you need multi-threading?" to determine whether it is really required; this may save you many headaches.<br />
<br />
One of the threads is called the Main Thread. The Main Thread is the one that is created by the Operating System once our application starts. The Main Thread '''must be''' the only thread that updates the components that interfaces with the user: otherwise, the application may hang.<br />
<br />
The main idea is that the application can do some processing in background in a second thread while the user can continue working using the main thread.<br />
<br />
Another use of threads is just to have a better responding application. If you create an application, and when the user presses a button the application starts processing a big job... and while processing, the screen stops responding, and gives the user the impression that the application is frozen, a poor or misleading impression will be created. If the big job runs in a second thread, the application keeps responding (almost) as if it were idle. In this case it is a good idea, before starting the thread, to disable the buttons of the form to avoid the user starting more than one thread for the job.<br />
<br />
Another use of multi-threading may be a server application that is able to respond to many clients at the same time.<br />
<br />
== Do you need multi-threading? ==<br />
<br />
If you are new to multi-threading and you only want to make your application more responsive while your application performs moderately long-running tasks, then multi-threading may be more than is required.<br />
Multi-threaded applications are always more difficult to debug and they are often much more complex; in many cases you don't need multi-threading. A single thread is enough. If you can split up the time-consuming task into several smaller chunks, then instead you should use '''Application.ProcessMessages'''. This method allows the LCL to handle all waiting messages and returns.<br />
The central idea is to call Application.ProcessMessages at regular intervals during the execution of a long-running task to determine whether the user has clicked on something, or a progress indicator must be repainted, and so on.<br />
<br />
For example: Reading a big file and process it. <br />
See examples/multithreading/singlethreadingexample1.lpi.<br />
<br />
Multi-threading is only needed for<br />
* blocking handles, like network communications<br />
* using multiple processors simultaneously (SMP)<br />
* algorithms and library calls that must be called through an API an as such cannot be split up into smaller parts.<br />
<br />
If You want to use multi-threading to increase speed by using multiple processors simultaneously, check if your current program now use all 100% resources of 1 core CPU (for example, you program can actively use input-output operations, e.g. writing to file; this takes a lot of time, but doesn't load CPU; in this case you program will not be faster with multiple threads). Also check if optimisation level is set to maximum (3). When I switched optimisation level from 1 to 3, my program became about 5 times faster.<br />
<br />
== The TThread Class ==<br />
<br />
The following example can be found in the examples/multithreading/ directory.<br />
<br />
To create a multi-threaded application, the easiest way is to use the TThread Class. This class permits the creation of an additional thread (alongside the main thread) in a simple way. Normally you are required to override only 2 methods: the Create constructor, and the Execute method.<br />
<br />
In the constructor, you will prepare the thread to run. You will set the initial values of the variables or properties you need. The original constructor of TThread requires a parameter called Suspended. As you might expect, setting Suspended = True will prevent the thread starting automatically after the creation. If Suspended = False, the thread will start running just after the creation. If the thread is created suspended, then it will run only after the Resume method is called.<br />
<br />
As of FPC version 2.0.1 and later, TThread.Create also has an implicit parameter for Stack Size. You can now change the default stack size of each thread you create if you need it. Deep procedure call recursions in a thread are a good example. If you don't specify the stack size parameter, a default OS stack size is used.<br />
<br />
In the overridden Execute method you will write the code that will run on the thread.<br />
<br />
The TThread class has one important property: <br />
Terminated : boolean;<br />
<br />
If the thread has a loop (and this is typical), the loop should be exited when Terminated is true (it is false by default). Within each pass, the value of Terminated must be checked, and if it is true then the loop should be exited as quickly as is appropriate, after any necessary cleanup. Bear in mind that the Terminate method does not do anything by default: the .Execute method must explicitly implement support for it to quit its job.<br />
<br />
As we explained earlier, the thread should not interact with the visible components. Updates to visible components must be made within the context of the main thread. To do this, a TThread method called Synchronize exists. Synchronize requires a method (that takes no parameters) as an argument. When you call that method through Synchronize(@MyMethod), the thread execution will be paused, the code of MyMethod will run in the main thread, and then the thread execution will be resumed. The exact working of Synchronize depends on the platform, but basically it does this: it posts a message onto the main message queue and goes to sleep. Eventually the main thread processes the message and calls MyMethod. This way MyMethod is called without context, that means not during a mouse down event or during paint event, but after. After the main thread executed MyMethod, it wakes the sleeping Thread and processes the next message. The Thread then continues.<br />
<br />
There is another important property of TThread: FreeOnTerminate. If this property is true, the thread object is automatically freed when the thread execution (.Execute method) stops. Otherwise the application will need to free it manually.<br />
<br />
Example:<br />
<br />
<delphi> Type<br />
TMyThread = class(TThread)<br />
private<br />
fStatusText : string;<br />
procedure ShowStatus;<br />
protected<br />
procedure Execute; override;<br />
public<br />
Constructor Create(CreateSuspended : boolean);<br />
end;<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.ShowStatus;<br />
// this method is executed by the mainthread and can therefore access all GUI elements.<br />
begin<br />
Form1.Caption := fStatusText;<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
newStatus : string;<br />
begin<br />
fStatusText := 'TMyThread Starting...';<br />
Synchronize(@Showstatus);<br />
fStatusText := 'TMyThread Running...';<br />
while (not Terminated) and ([any condition required]) do<br />
begin<br />
...<br />
[here goes the code of the main thread loop]<br />
...<br />
if NewStatus <> fStatusText then<br />
begin<br />
fStatusText := newStatus;<br />
Synchronize(@Showstatus);<br />
end;<br />
end;<br />
end;<br />
</delphi><br />
<br />
On the application,<br />
<br />
<delphi><br />
var<br />
MyThread : TMyThread;<br />
begin<br />
MyThread := TMyThread.Create(True); // This way it doesn't start automatically<br />
...<br />
[Here the code initialises anything required before the threads starts executing]<br />
...<br />
MyThread.Resume;<br />
end;</delphi><br />
<br />
If you want to make your application more flexible you can create an event for the thread; this way your synchronized method won't be tightly coupled with a specific form or class: you can attach listeners to the thread's event. Here is an example:<br />
<br />
<delphi> Type<br />
TShowStatusEvent = procedure(Status: String) of Object;<br />
<br />
TMyThread = class(TThread)<br />
private<br />
fStatusText : string;<br />
FOnShowStatus: TShowStatusEvent;<br />
procedure ShowStatus;<br />
protected<br />
procedure Execute; override;<br />
public<br />
Constructor Create(CreateSuspended : boolean);<br />
property OnShowStatus: TShowStatusEvent read FOnShowStatus write FOnShowStatus;<br />
end;<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.ShowStatus;<br />
// this method is executed by the mainthread and can therefore access all GUI elements.<br />
begin<br />
if Assigned(FOnShowStatus) then<br />
begin<br />
FOnShowStatus(fStatusText);<br />
end;<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
newStatus : string;<br />
begin<br />
fStatusText := 'TMyThread Starting...';<br />
Synchronize(@Showstatus);<br />
fStatusText := 'TMyThread Running...';<br />
while (not Terminated) and ([any condition required]) do<br />
begin<br />
...<br />
[here goes the code of the main thread loop]<br />
...<br />
if NewStatus <> fStatusText then<br />
begin<br />
fStatusText := newStatus;<br />
Synchronize(@Showstatus);<br />
end;<br />
end;<br />
end;<br />
</delphi><br />
<br />
On the application,<br />
<br />
<delphi> Type<br />
TForm1 = class(TForm)<br />
Button1: TButton;<br />
Label1: TLabel;<br />
procedure FormCreate(Sender: TObject);<br />
procedure FormDestroy(Sender: TObject);<br />
private<br />
{ private declarations }<br />
MyThread: TMyThread; <br />
procedure ShowStatus(Status: string);<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
procedure TForm1.FormCreate(Sender: TObject);<br />
begin<br />
inherited;<br />
MyThread := TMyThread.Create(true);<br />
MyThread.OnShowStatus := @ShowStatus;<br />
end;<br />
<br />
procedure TForm1.FormDestroy(Sender: TObject);<br />
begin<br />
MyThread.Terminate;<br />
MyThread.Free;<br />
inherited;<br />
end;<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
begin<br />
MyThread.Resume;<br />
end;<br />
<br />
procedure TForm1.ShowStatus(Status: string);<br />
begin<br />
Label1.Caption := Status;<br />
end;</delphi><br />
<br />
== Special things to take care of ==<br />
<br />
=== Stack checking under Windows ===<br />
<br />
There is a potential headache in Windows with Threads if you use the -Ct (stack check) switch.<br />
For reasons not so clear the stack check will "trigger" on any TThread.Create if you use the default stack size.<br />
The only work-around for the moment is to simply not use -Ct switch. Note that it does NOT cause an exception in<br />
the main thread, but in the newly created one. This "looks" like if the thread was never started.<br />
<br />
A good code to check for this and other exceptions which can occur in thread creation is:<br />
<br />
<delphi>MyThread := TThread.Create(False);<br />
if Assigned(MyThread.FatalException) then<br />
raise MyThread.FatalException;</delphi><br />
<br />
This code will assure that any exception which occurred during thread creation will be raised in your main thread.<br />
<br />
== Units needed for a multi-threaded application ==<br />
You don´t need any special unit for this to work with Windows.<br />
However with Linux, Mac OS X and FreeBSD, you need the cthreads unit and it ''must'' be the first used unit of the project (the program unit, .lpr)!<br />
<br />
So, your Lazarus application code should look like:<br />
<br />
<Delphi><br />
program MyMultiThreadedProgram;<br />
{$mode objfpc}{$H+}<br />
uses<br />
{$ifdef unix}<br />
cthreads,<br />
cmem, // the c memory manager is on some systems much faster for multi-threading<br />
{$endif}<br />
Interfaces, // this includes the LCL widgetset<br />
Forms<br />
{ you can add units here },<br />
</Delphi><br />
<br />
If you forget this and you use TThread you will get this error on startup:<br />
This binary has no thread support compiled in.<br />
Recompile the application with a thread-driver in the program uses clause before other units using thread.<br />
<br />
=== Multithreading in packages ===<br />
Packages which uses multi-threading should add the '''-dUseCThreads''' flag to the custom usage options. Open the package editor of the package, then Options > Usage > Custom and add ''-dUseCThreads''. This will define this flag to all projects and packages using this package, including the IDE. The IDE and all new applications created by the IDE have already the following code in their .lpr file:<br />
<Delphi><br />
uses<br />
{$IFDEF UNIX}{$IFDEF UseCThreads}<br />
cthreads,<br />
cmem, // the c memory manager is on some systems much faster for multi-threading<br />
{$ENDIF}{$ENDIF}<br />
</DELPHI><br />
<br />
===Heaptrc===<br />
<br />
You can not use the -gh switch with the ''cmem'' unit. The -gh switch uses the heaptrc unit, which extends the heap manager. Therefore the '''heaptrc''' unit must be used '''after''' the '''cmem''' unit.<br />
<br />
<Delphi>uses<br />
{$IFDEF UNIX}{$IFDEF UseCThreads}<br />
cthreads,<br />
cmem, // the c memory manager is on some systems much faster for multi-threading<br />
{$ENDIF}{$ENDIF}<br />
heaptrc,</DELPHI><br />
<br />
== SMP Support ==<br />
The good news is that if your application works properly multi-threaded this way, it is already SMP enabled!<br />
<br />
== Debugging Multi-threaded Applications with Lazarus ==<br />
The debugging on Lazarus requires GDB and is rapidly becoming more and more fully featured and stable. However, there still exists a few Linux distributions with some problems. <br />
<br />
=== Debugging output ===<br />
In a single threaded application, you can simply write to console/terminal/whatever and the order of the lines is the same as they were written.<br />
In multi-threaded application things are more complicated. If two threads are writing, say a line is written by thread A before a line by thread B, then the lines are not necessarily written in that order. It can even happen, that a thread writes its output, while the other thread is writing a line.<br />
<br />
The LCLProc unit contains several functions, to let each thread write to its own log file:<br />
<delphi> procedure DbgOutThreadLog(const Msg: string); overload;<br />
procedure DebuglnThreadLog(const Msg: string); overload;<br />
procedure DebuglnThreadLog(Args: array of const); overload;<br />
procedure DebuglnThreadLog; overload;</delphi><br />
<br />
For example:<br />
Instead of ''writeln('Some text ',123);'' use <br />
DebuglnThreadLog(['Some text ',123]);<br />
<br />
This will append a line 'Some text 123' to '''Log<PID>.txt''', where <PID> is the process ID of the current thread.<br />
<br />
It is a good idea to remove the log files before each run:<br />
rm -f Log* && ./project1<br />
<br />
=== Linux ===<br />
If you try to debug a multi-threaded application on Linux, you will have one big problem: the Desktop Manager on X server will hang. When that happens you can simply exit out of that session and create a new session by pressing CTRL+ALT+F3. That will give you a new session prompt. Once there enter sudo /etc/init.d/gdm restart. This will restart the desktop manager and get you back into your desktop.<br />
<br />
One method that solved this problem for Ubuntu x64 is to set the Project options for debugging required extra information file...<br />
<br />
Project Options -> Compiler Options -> Linking -> Debugging: Check Use external gdb debug symbols file (-Xg).<br />
<br />
If the above solution does not work a workaround is:<br />
<br />
Create a new instance of X with:<br />
<br />
X :1 &<br />
<br />
It will open, and when you switch to another desktop (the one you are working with pressing CTRL+ALT+F7), you will be able to go back to the new graphical desktop with CTRL+ALT+F8 (if this combination does not work, try with CTRL+ALT+F2... this one worked on [http://www.slackware.com Slackware]).<br />
<br />
Then you could, if you want, create a desktop session on the X started with:<br />
<br />
gnome-session --display=:1 &<br />
<br />
Then, in Lazarus, on the run parameters dialog for the project, check "Use display" and enter :1.<br />
<br />
Now the application will run on the second X server and you will be able to debug it on the first one.<br />
<br />
This was tested with Free Pascal 2.0 and Lazarus 0.9.10 on Windows and Linux.<br />
<br />
<br />
----<br />
<br />
Instead of creating a new X session, one can use [http://en.wikipedia.org/wiki/Xnest Xnest]. Xnest is a X session on a window. Using it X server didn't lock while debugging threads, and it's much easier to debug without keeping changing terminals.<br />
<br />
The command line to run Xnest is<br />
<br />
Xnest :1 -ac<br />
<br />
to create a X session on :1, and disabling access control.<br />
<br />
== Widgetsets ==<br />
The win32, the gtk and the carbon interfaces fully support multi-threading. This means, TThread, critical sections and Synchronize work.<br />
<br />
== Critical sections ==<br />
A ''critical section'' is an object used to make sure, that some part of the code is executed only by one thread at a time. A critical section needs to be created/initialized before it can be used and be freed when it is not needed anymore.<br />
<br />
Critical sections are normally used this way:<br />
<br />
Add the unit SyncObjs.<br />
<br />
Declare the section (globally for all threads which should access the section):<br />
MyCriticalSection: TRTLCriticalSection;<br />
<br />
Create the section:<br />
InitializeCriticalSection(MyCriticalSection);<br />
<br />
Run some threads. Doing something exclusively<br />
<delphi><br />
EnterCriticalSection(MyCriticalSection);<br />
try<br />
// access some variables, write files, send some network packets, etc<br />
finally<br />
LeaveCriticalSection(MyCriticalSection);<br />
end;</delphi><br />
<br />
After all threads terminated, free it:<br />
DeleteCriticalSection(MyCriticalSection);<br />
<br />
As an alternative, you can use a TCriticalSection object. The creation does the initialization, the Enter method does the EnterCriticalSection, the Leave method does the LeaveCriticalSection and the destruction of the object does the deletion.<br />
<br />
For example: 5 threads incrementing a counter.<br />
See lazarus/examples/multithreading/criticalsectionexample1.lpi<br />
<br />
'''BEWARE:''' There are two sets of the above 4 functions. The RTL and the LCL ones. The LCL ones are defined in the unit LCLIntf and LCLType. Both work pretty much the same. You can use both at the same time in your application, but you should not use a RTL function with an LCL Critical Section and vice versus.<br />
<br />
<br />
=== Sharing Variables ===<br />
If some threads share a variable, that is read only, then there is nothing to worry about. Just read it.<br />
But if one or several threads changes the variable, then you must make sure, that only one thread accesses the variables at a time.<br />
<br />
For example: 5 threads incrementing a counter.<br />
See lazarus/examples/multithreading/criticalsectionexample1.lpi<br />
<br />
== Waiting for another thread ==<br />
If a thread A needs a result of another thread B, it must wait, till B has finished. <br />
<br />
'''Important:''' The main thread should never wait for another thread. Instead use Synchronize (see above).<br />
<br />
See for an example: lazarus/examples/multithreading/waitforexample1.lpi<br />
<br />
<delphi>{ TThreadA }<br />
<br />
procedure TThreadA.Execute;<br />
begin<br />
Form1.ThreadB:=TThreadB.Create(false);<br />
// create event<br />
WaitForB:=RTLEventCreate;<br />
while not Application.Terminated do begin<br />
// wait infinitely (until B wakes A)<br />
RtlEventWaitFor(WaitForB);<br />
writeln('A: ThreadB.Counter='+IntToStr(Form1.ThreadB.Counter));<br />
end;<br />
end;<br />
<br />
{ TThreadB }<br />
<br />
procedure TThreadB.Execute;<br />
var<br />
i: Integer;<br />
begin<br />
Counter:=0;<br />
while not Application.Terminated do begin<br />
// B: Working ...<br />
Sleep(1500);<br />
inc(Counter);<br />
// wake A<br />
RtlEventSetEvent(Form1.ThreadA.WaitForB);<br />
end;<br />
end;</delphi><br />
<br />
Note: RtlEventSetEvent can be called before RtlEventWaitFor. Then RtlEventWaitFor will return immediately. Use RTLeventResetEvent to clear a flag.<br />
<br />
== Fork ==<br />
When forking in a multi-threaded application, be aware that any threads created and running BEFORE the fork (or fpFork) call, will NOT be running in the child process. As stated on the fork() man page, any threads that were running before the fork call, their state will be undefined.<br />
<br />
So be aware of any threads initializing before the call (including on the initialization section). They will NOT work.<br />
<br />
== Parallel procedures/loops ==<br />
A special case of multi threading is running a single procedure in parallel. See [[Parallel procedures]].<br />
<br />
== Distributed computing ==<br />
The next higher steps after multi threading is running the threads on multiple machines. <br />
* You can use one of the TCP suites like synapse, lnet or indy for communications. This gives you maximum flexibility and is mostly used for loosely connected Client / Server applications.<br />
* You can use message passing libraries like [[MPICH]], which are used for HPC (High Performance Computing) on clusters.<br />
<br />
<br />
<br />
== External threads ==<br />
To make Free Pascal's threading system to work properly, each newly created FPC thread needs to be initialized (more exactly, the exception, I/O system and threadvar system per thread needs to be initialized so threadvars and heap are working). That is fully automatically done for you if you use BeginThread (or indirectly by using the TThread class). However, if you use threads that were created without BeginThread (i.e. external threads), additional work (currently) might be required. External threads also include those that were created in external C libraries (.DLL/.so). <br />
<br />
<br />
Things to consider when using external threads (might not be needed in all or future compiler versions): <br />
<br />
* Do not use external threads at all - use FPC threads. If can you can get control over how the thread is created, create the thread by yourself by using BeginThread.<br />
<br />
If the calling convention doesn't fit (e.g. if your original thread function needs cdecl calling convention but BeginThread needs pascal convention, create a record, store the original required thread function in it, and call that function in your pascal thread function: <br />
<br />
<Delphi>type <br />
TCdeclThreadFunc = function (user_data:Pointer):Pointer;cdecl; <br />
<br />
PCdeclThreadFuncData = ^TCdeclThreadFunc; <br />
TCdeclThreadFuncData = record<br />
Func: TCdeclThreadFunc; //cdecl function<br />
Data: Pointer; //original data<br />
end; <br />
<br />
// The Pascal thread calls the cdecl function<br />
function C2P_Translator(FuncData: pointer) : ptrint;<br />
var<br />
ThreadData: TCdeclThreadFuncData;<br />
begin <br />
ThreadData := PCdeclThreadFuncData(FuncData)^;<br />
Result := ptrint(ThreadData.Func(ThreadData.Data));<br />
end; <br />
<br />
procedure CreatePascalThread;<br />
var<br />
ThreadData: PCdeclThreadFunc; <br />
begin <br />
New(ThreadData);<br />
// this is the desired cdecl thread function<br />
ThreadData^.Func := func; <br />
ThreadData^.Data := user_data; <br />
// this creates the Pascal thread<br />
BeginThread(@C2P_Translator, ThreadData );<br />
end;</Delphi><br />
<br />
<br />
* Initialize the FPC's threading system by creating a dummy thread. If you don't create any Pascal thread in your app, the thread system won't be initialized (and thus threadvars won't work and thus heap will not work correctly). <br />
<br />
<Delphi>type<br />
tc = class(tthread)<br />
procedure execute;override;<br />
end;<br />
<br />
procedure tc.execute;<br />
begin<br />
end;<br />
<br />
{ main program } <br />
begin<br />
{ initialise threading system }<br />
with tc.create(false) do<br />
begin<br />
waitfor;<br />
free;<br />
end;<br />
{ ... your code follows } <br />
end.</Delphi><br />
<br />
(After the threading system is initialized, the runtime may set the system variable "IsMultiThread" to true which is used by FPC routines to perform locks here and there. You should not set this variable manually.) <br />
<br />
<br />
* If for some reason this doesn't work for you, try this code in your external thread function:<br />
<br />
<Delphi>function ExternalThread(param: Pointer): LongInt; stdcall;<br />
var<br />
tm: TThreadManager;<br />
begin<br />
GetThreadManager(tm);<br />
tm.AllocateThreadVars;<br />
InitThread(1000000); // adjust inital stack size here<br />
<br />
{ do something threaded here ... }<br />
<br />
Result:=0;<br />
end;</Delphi><br />
<br />
<br />
=== Identifying external threads ===<br />
Sometimes you even don't know if you have to deal with external threads (e.g. if some C library makes a callback). This can help to analyse this: <br />
<br />
1. Ask the OS for the ID of the current thread at your application's start <br />
<br />
<Delphi>Win32: GetCurrentThreadID();<br />
Darwin: GetThreadID(); <br />
Linux: TThreadID(pthread_self);</Delphi><br />
<br />
2. Ask again for the ID of the current thread inside the thread function and compare this by the result of step 1.<br />
<br />
==See also==<br />
<br />
* [[Streaming components]]<br />
* [[Manager Worker Threads System]]<br />
* [[Example of multi-threaded application: array of threads]]<br />
* [[Parallel procedures]]<br />
<br />
[[Category:Tutorials]]</div>Mvampirehttps://wiki.freepascal.org/index.php?title=Multithreaded_Application_Tutorial&diff=45217Multithreaded Application Tutorial2010-09-23T10:49:08Z<p>Mvampire: /* Do you need multi-threading? */</p>
<hr />
<div>{{Multithreaded Application Tutorial}}<br />
<br />
== Overview ==<br />
This page will try to explain how to write and debug a multi-threaded application with Free Pascal and Lazarus. A multi-threaded application is one that creates two or more threads of execution that work at the same time. If you are new to multi-threading, please read the paragraph "Do you need multi-threading?" to determine whether it is really required; this may save you many headaches.<br />
<br />
One of the threads is called the Main Thread. The Main Thread is the one that is created by the Operating System once our application starts. The Main Thread '''must be''' the only thread that updates the components that interfaces with the user: otherwise, the application may hang.<br />
<br />
The main idea is that the application can do some processing in background in a second thread while the user can continue working using the main thread.<br />
<br />
Another use of threads is just to have a better responding application. If you create an application, and when the user presses a button the application starts processing a big job... and while processing, the screen stops responding, and gives the user the impression that the application is frozen, a poor or misleading impression will be created. If the big job runs in a second thread, the application keeps responding (almost) as if it were idle. In this case it is a good idea, before starting the thread, to disable the buttons of the form to avoid the user starting more than one thread for the job.<br />
<br />
Another use of multi-threading may be a server application that is able to respond to many clients at the same time.<br />
<br />
== Do you need multi-threading? ==<br />
<br />
If you are new to multi-threading and you only want to make your application more responsive while your application performs moderately long-running tasks, then multi-threading may be more than is required.<br />
Multi-threaded applications are always more difficult to debug and they are often much more complex; in many cases you don't need multi-threading. A single thread is enough. If you can split up the time-consuming task into several smaller chunks, then instead you should use '''Application.ProcessMessages'''. This method allows the LCL to handle all waiting messages and returns.<br />
The central idea is to call Application.ProcessMessages at regular intervals during the execution of a long-running task to determine whether the user has clicked on something, or a progress indicator must be repainted, and so on.<br />
<br />
For example: Reading a big file and process it. <br />
See examples/multithreading/singlethreadingexample1.lpi.<br />
<br />
Multi-threading is only needed for<br />
* blocking handles, like network communications<br />
* using multiple processors simultaneously (SMP)<br />
* algorithms and library calls that must be called through an API an as such cannot be split up into smaller parts.<br />
<br />
If You want to use multi-threading to increase speed by using multiple processors simultaneously, check if your current program now use all 100% resources of 1 core CPU (for example, you program can actively use input-output operations, e.g. writing to file; this takes a lot of time, but doesn't load CPU; in this case you program will not be faster with multiple threads). Also check if optimisation level is set to maximum (3). When I switched optimisation level from 1 to 3, my program become about 5 times faster.<br />
<br />
== The TThread Class ==<br />
<br />
The following example can be found in the examples/multithreading/ directory.<br />
<br />
To create a multi-threaded application, the easiest way is to use the TThread Class. This class permits the creation of an additional thread (alongside the main thread) in a simple way. Normally you are required to override only 2 methods: the Create constructor, and the Execute method.<br />
<br />
In the constructor, you will prepare the thread to run. You will set the initial values of the variables or properties you need. The original constructor of TThread requires a parameter called Suspended. As you might expect, setting Suspended = True will prevent the thread starting automatically after the creation. If Suspended = False, the thread will start running just after the creation. If the thread is created suspended, then it will run only after the Resume method is called.<br />
<br />
As of FPC version 2.0.1 and later, TThread.Create also has an implicit parameter for Stack Size. You can now change the default stack size of each thread you create if you need it. Deep procedure call recursions in a thread are a good example. If you don't specify the stack size parameter, a default OS stack size is used.<br />
<br />
In the overridden Execute method you will write the code that will run on the thread.<br />
<br />
The TThread class has one important property: <br />
Terminated : boolean;<br />
<br />
If the thread has a loop (and this is typical), the loop should be exited when Terminated is true (it is false by default). Within each pass, the value of Terminated must be checked, and if it is true then the loop should be exited as quickly as is appropriate, after any necessary cleanup. Bear in mind that the Terminate method does not do anything by default: the .Execute method must explicitly implement support for it to quit its job.<br />
<br />
As we explained earlier, the thread should not interact with the visible components. Updates to visible components must be made within the context of the main thread. To do this, a TThread method called Synchronize exists. Synchronize requires a method (that takes no parameters) as an argument. When you call that method through Synchronize(@MyMethod), the thread execution will be paused, the code of MyMethod will run in the main thread, and then the thread execution will be resumed. The exact working of Synchronize depends on the platform, but basically it does this: it posts a message onto the main message queue and goes to sleep. Eventually the main thread processes the message and calls MyMethod. This way MyMethod is called without context, that means not during a mouse down event or during paint event, but after. After the main thread executed MyMethod, it wakes the sleeping Thread and processes the next message. The Thread then continues.<br />
<br />
There is another important property of TThread: FreeOnTerminate. If this property is true, the thread object is automatically freed when the thread execution (.Execute method) stops. Otherwise the application will need to free it manually.<br />
<br />
Example:<br />
<br />
<delphi> Type<br />
TMyThread = class(TThread)<br />
private<br />
fStatusText : string;<br />
procedure ShowStatus;<br />
protected<br />
procedure Execute; override;<br />
public<br />
Constructor Create(CreateSuspended : boolean);<br />
end;<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.ShowStatus;<br />
// this method is executed by the mainthread and can therefore access all GUI elements.<br />
begin<br />
Form1.Caption := fStatusText;<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
newStatus : string;<br />
begin<br />
fStatusText := 'TMyThread Starting...';<br />
Synchronize(@Showstatus);<br />
fStatusText := 'TMyThread Running...';<br />
while (not Terminated) and ([any condition required]) do<br />
begin<br />
...<br />
[here goes the code of the main thread loop]<br />
...<br />
if NewStatus <> fStatusText then<br />
begin<br />
fStatusText := newStatus;<br />
Synchronize(@Showstatus);<br />
end;<br />
end;<br />
end;<br />
</delphi><br />
<br />
On the application,<br />
<br />
<delphi><br />
var<br />
MyThread : TMyThread;<br />
begin<br />
MyThread := TMyThread.Create(True); // This way it doesn't start automatically<br />
...<br />
[Here the code initialises anything required before the threads starts executing]<br />
...<br />
MyThread.Resume;<br />
end;</delphi><br />
<br />
If you want to make your application more flexible you can create an event for the thread; this way your synchronized method won't be tightly coupled with a specific form or class: you can attach listeners to the thread's event. Here is an example:<br />
<br />
<delphi> Type<br />
TShowStatusEvent = procedure(Status: String) of Object;<br />
<br />
TMyThread = class(TThread)<br />
private<br />
fStatusText : string;<br />
FOnShowStatus: TShowStatusEvent;<br />
procedure ShowStatus;<br />
protected<br />
procedure Execute; override;<br />
public<br />
Constructor Create(CreateSuspended : boolean);<br />
property OnShowStatus: TShowStatusEvent read FOnShowStatus write FOnShowStatus;<br />
end;<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.ShowStatus;<br />
// this method is executed by the mainthread and can therefore access all GUI elements.<br />
begin<br />
if Assigned(FOnShowStatus) then<br />
begin<br />
FOnShowStatus(fStatusText);<br />
end;<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
newStatus : string;<br />
begin<br />
fStatusText := 'TMyThread Starting...';<br />
Synchronize(@Showstatus);<br />
fStatusText := 'TMyThread Running...';<br />
while (not Terminated) and ([any condition required]) do<br />
begin<br />
...<br />
[here goes the code of the main thread loop]<br />
...<br />
if NewStatus <> fStatusText then<br />
begin<br />
fStatusText := newStatus;<br />
Synchronize(@Showstatus);<br />
end;<br />
end;<br />
end;<br />
</delphi><br />
<br />
On the application,<br />
<br />
<delphi> Type<br />
TForm1 = class(TForm)<br />
Button1: TButton;<br />
Label1: TLabel;<br />
procedure FormCreate(Sender: TObject);<br />
procedure FormDestroy(Sender: TObject);<br />
private<br />
{ private declarations }<br />
MyThread: TMyThread; <br />
procedure ShowStatus(Status: string);<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
procedure TForm1.FormCreate(Sender: TObject);<br />
begin<br />
inherited;<br />
MyThread := TMyThread.Create(true);<br />
MyThread.OnShowStatus := @ShowStatus;<br />
end;<br />
<br />
procedure TForm1.FormDestroy(Sender: TObject);<br />
begin<br />
MyThread.Terminate;<br />
MyThread.Free;<br />
inherited;<br />
end;<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
begin<br />
MyThread.Resume;<br />
end;<br />
<br />
procedure TForm1.ShowStatus(Status: string);<br />
begin<br />
Label1.Caption := Status;<br />
end;</delphi><br />
<br />
== Special things to take care of ==<br />
<br />
=== Stack checking under Windows ===<br />
<br />
There is a potential headache in Windows with Threads if you use the -Ct (stack check) switch.<br />
For reasons not so clear the stack check will "trigger" on any TThread.Create if you use the default stack size.<br />
The only work-around for the moment is to simply not use -Ct switch. Note that it does NOT cause an exception in<br />
the main thread, but in the newly created one. This "looks" like if the thread was never started.<br />
<br />
A good code to check for this and other exceptions which can occur in thread creation is:<br />
<br />
<delphi>MyThread := TThread.Create(False);<br />
if Assigned(MyThread.FatalException) then<br />
raise MyThread.FatalException;</delphi><br />
<br />
This code will assure that any exception which occurred during thread creation will be raised in your main thread.<br />
<br />
== Units needed for a multi-threaded application ==<br />
You don´t need any special unit for this to work with Windows.<br />
However with Linux, Mac OS X and FreeBSD, you need the cthreads unit and it ''must'' be the first used unit of the project (the program unit, .lpr)!<br />
<br />
So, your Lazarus application code should look like:<br />
<br />
<Delphi><br />
program MyMultiThreadedProgram;<br />
{$mode objfpc}{$H+}<br />
uses<br />
{$ifdef unix}<br />
cthreads,<br />
cmem, // the c memory manager is on some systems much faster for multi-threading<br />
{$endif}<br />
Interfaces, // this includes the LCL widgetset<br />
Forms<br />
{ you can add units here },<br />
</Delphi><br />
<br />
If you forget this and you use TThread you will get this error on startup:<br />
This binary has no thread support compiled in.<br />
Recompile the application with a thread-driver in the program uses clause before other units using thread.<br />
<br />
=== Multithreading in packages ===<br />
Packages which uses multi-threading should add the '''-dUseCThreads''' flag to the custom usage options. Open the package editor of the package, then Options > Usage > Custom and add ''-dUseCThreads''. This will define this flag to all projects and packages using this package, including the IDE. The IDE and all new applications created by the IDE have already the following code in their .lpr file:<br />
<Delphi><br />
uses<br />
{$IFDEF UNIX}{$IFDEF UseCThreads}<br />
cthreads,<br />
cmem, // the c memory manager is on some systems much faster for multi-threading<br />
{$ENDIF}{$ENDIF}<br />
</DELPHI><br />
<br />
===Heaptrc===<br />
<br />
You can not use the -gh switch with the ''cmem'' unit. The -gh switch uses the heaptrc unit, which extends the heap manager. Therefore the '''heaptrc''' unit must be used '''after''' the '''cmem''' unit.<br />
<br />
<Delphi>uses<br />
{$IFDEF UNIX}{$IFDEF UseCThreads}<br />
cthreads,<br />
cmem, // the c memory manager is on some systems much faster for multi-threading<br />
{$ENDIF}{$ENDIF}<br />
heaptrc,</DELPHI><br />
<br />
== SMP Support ==<br />
The good news is that if your application works properly multi-threaded this way, it is already SMP enabled!<br />
<br />
== Debugging Multi-threaded Applications with Lazarus ==<br />
The debugging on Lazarus requires GDB and is rapidly becoming more and more fully featured and stable. However, there still exists a few Linux distributions with some problems. <br />
<br />
=== Debugging output ===<br />
In a single threaded application, you can simply write to console/terminal/whatever and the order of the lines is the same as they were written.<br />
In multi-threaded application things are more complicated. If two threads are writing, say a line is written by thread A before a line by thread B, then the lines are not necessarily written in that order. It can even happen, that a thread writes its output, while the other thread is writing a line.<br />
<br />
The LCLProc unit contains several functions, to let each thread write to its own log file:<br />
<delphi> procedure DbgOutThreadLog(const Msg: string); overload;<br />
procedure DebuglnThreadLog(const Msg: string); overload;<br />
procedure DebuglnThreadLog(Args: array of const); overload;<br />
procedure DebuglnThreadLog; overload;</delphi><br />
<br />
For example:<br />
Instead of ''writeln('Some text ',123);'' use <br />
DebuglnThreadLog(['Some text ',123]);<br />
<br />
This will append a line 'Some text 123' to '''Log<PID>.txt''', where <PID> is the process ID of the current thread.<br />
<br />
It is a good idea to remove the log files before each run:<br />
rm -f Log* && ./project1<br />
<br />
=== Linux ===<br />
If you try to debug a multi-threaded application on Linux, you will have one big problem: the Desktop Manager on X server will hang. When that happens you can simply exit out of that session and create a new session by pressing CTRL+ALT+F3. That will give you a new session prompt. Once there enter sudo /etc/init.d/gdm restart. This will restart the desktop manager and get you back into your desktop.<br />
<br />
One method that solved this problem for Ubuntu x64 is to set the Project options for debugging required extra information file...<br />
<br />
Project Options -> Compiler Options -> Linking -> Debugging: Check Use external gdb debug symbols file (-Xg).<br />
<br />
If the above solution does not work a workaround is:<br />
<br />
Create a new instance of X with:<br />
<br />
X :1 &<br />
<br />
It will open, and when you switch to another desktop (the one you are working with pressing CTRL+ALT+F7), you will be able to go back to the new graphical desktop with CTRL+ALT+F8 (if this combination does not work, try with CTRL+ALT+F2... this one worked on [http://www.slackware.com Slackware]).<br />
<br />
Then you could, if you want, create a desktop session on the X started with:<br />
<br />
gnome-session --display=:1 &<br />
<br />
Then, in Lazarus, on the run parameters dialog for the project, check "Use display" and enter :1.<br />
<br />
Now the application will run on the second X server and you will be able to debug it on the first one.<br />
<br />
This was tested with Free Pascal 2.0 and Lazarus 0.9.10 on Windows and Linux.<br />
<br />
<br />
----<br />
<br />
Instead of creating a new X session, one can use [http://en.wikipedia.org/wiki/Xnest Xnest]. Xnest is a X session on a window. Using it X server didn't lock while debugging threads, and it's much easier to debug without keeping changing terminals.<br />
<br />
The command line to run Xnest is<br />
<br />
Xnest :1 -ac<br />
<br />
to create a X session on :1, and disabling access control.<br />
<br />
== Widgetsets ==<br />
The win32, the gtk and the carbon interfaces fully support multi-threading. This means, TThread, critical sections and Synchronize work.<br />
<br />
== Critical sections ==<br />
A ''critical section'' is an object used to make sure, that some part of the code is executed only by one thread at a time. A critical section needs to be created/initialized before it can be used and be freed when it is not needed anymore.<br />
<br />
Critical sections are normally used this way:<br />
<br />
Add the unit SyncObjs.<br />
<br />
Declare the section (globally for all threads which should access the section):<br />
MyCriticalSection: TRTLCriticalSection;<br />
<br />
Create the section:<br />
InitializeCriticalSection(MyCriticalSection);<br />
<br />
Run some threads. Doing something exclusively<br />
<delphi><br />
EnterCriticalSection(MyCriticalSection);<br />
try<br />
// access some variables, write files, send some network packets, etc<br />
finally<br />
LeaveCriticalSection(MyCriticalSection);<br />
end;</delphi><br />
<br />
After all threads terminated, free it:<br />
DeleteCriticalSection(MyCriticalSection);<br />
<br />
As an alternative, you can use a TCriticalSection object. The creation does the initialization, the Enter method does the EnterCriticalSection, the Leave method does the LeaveCriticalSection and the destruction of the object does the deletion.<br />
<br />
For example: 5 threads incrementing a counter.<br />
See lazarus/examples/multithreading/criticalsectionexample1.lpi<br />
<br />
'''BEWARE:''' There are two sets of the above 4 functions. The RTL and the LCL ones. The LCL ones are defined in the unit LCLIntf and LCLType. Both work pretty much the same. You can use both at the same time in your application, but you should not use a RTL function with an LCL Critical Section and vice versus.<br />
<br />
<br />
=== Sharing Variables ===<br />
If some threads share a variable, that is read only, then there is nothing to worry about. Just read it.<br />
But if one or several threads changes the variable, then you must make sure, that only one thread accesses the variables at a time.<br />
<br />
For example: 5 threads incrementing a counter.<br />
See lazarus/examples/multithreading/criticalsectionexample1.lpi<br />
<br />
== Waiting for another thread ==<br />
If a thread A needs a result of another thread B, it must wait, till B has finished. <br />
<br />
'''Important:''' The main thread should never wait for another thread. Instead use Synchronize (see above).<br />
<br />
See for an example: lazarus/examples/multithreading/waitforexample1.lpi<br />
<br />
<delphi>{ TThreadA }<br />
<br />
procedure TThreadA.Execute;<br />
begin<br />
Form1.ThreadB:=TThreadB.Create(false);<br />
// create event<br />
WaitForB:=RTLEventCreate;<br />
while not Application.Terminated do begin<br />
// wait infinitely (until B wakes A)<br />
RtlEventWaitFor(WaitForB);<br />
writeln('A: ThreadB.Counter='+IntToStr(Form1.ThreadB.Counter));<br />
end;<br />
end;<br />
<br />
{ TThreadB }<br />
<br />
procedure TThreadB.Execute;<br />
var<br />
i: Integer;<br />
begin<br />
Counter:=0;<br />
while not Application.Terminated do begin<br />
// B: Working ...<br />
Sleep(1500);<br />
inc(Counter);<br />
// wake A<br />
RtlEventSetEvent(Form1.ThreadA.WaitForB);<br />
end;<br />
end;</delphi><br />
<br />
Note: RtlEventSetEvent can be called before RtlEventWaitFor. Then RtlEventWaitFor will return immediately. Use RTLeventResetEvent to clear a flag.<br />
<br />
== Fork ==<br />
When forking in a multi-threaded application, be aware that any threads created and running BEFORE the fork (or fpFork) call, will NOT be running in the child process. As stated on the fork() man page, any threads that were running before the fork call, their state will be undefined.<br />
<br />
So be aware of any threads initializing before the call (including on the initialization section). They will NOT work.<br />
<br />
== Parallel procedures/loops ==<br />
A special case of multi threading is running a single procedure in parallel. See [[Parallel procedures]].<br />
<br />
== Distributed computing ==<br />
The next higher steps after multi threading is running the threads on multiple machines. <br />
* You can use one of the TCP suites like synapse, lnet or indy for communications. This gives you maximum flexibility and is mostly used for loosely connected Client / Server applications.<br />
* You can use message passing libraries like [[MPICH]], which are used for HPC (High Performance Computing) on clusters.<br />
<br />
<br />
<br />
== External threads ==<br />
To make Free Pascal's threading system to work properly, each newly created FPC thread needs to be initialized (more exactly, the exception, I/O system and threadvar system per thread needs to be initialized so threadvars and heap are working). That is fully automatically done for you if you use BeginThread (or indirectly by using the TThread class). However, if you use threads that were created without BeginThread (i.e. external threads), additional work (currently) might be required. External threads also include those that were created in external C libraries (.DLL/.so). <br />
<br />
<br />
Things to consider when using external threads (might not be needed in all or future compiler versions): <br />
<br />
* Do not use external threads at all - use FPC threads. If can you can get control over how the thread is created, create the thread by yourself by using BeginThread.<br />
<br />
If the calling convention doesn't fit (e.g. if your original thread function needs cdecl calling convention but BeginThread needs pascal convention, create a record, store the original required thread function in it, and call that function in your pascal thread function: <br />
<br />
<Delphi>type <br />
TCdeclThreadFunc = function (user_data:Pointer):Pointer;cdecl; <br />
<br />
PCdeclThreadFuncData = ^TCdeclThreadFunc; <br />
TCdeclThreadFuncData = record<br />
Func: TCdeclThreadFunc; //cdecl function<br />
Data: Pointer; //original data<br />
end; <br />
<br />
// The Pascal thread calls the cdecl function<br />
function C2P_Translator(FuncData: pointer) : ptrint;<br />
var<br />
ThreadData: TCdeclThreadFuncData;<br />
begin <br />
ThreadData := PCdeclThreadFuncData(FuncData)^;<br />
Result := ptrint(ThreadData.Func(ThreadData.Data));<br />
end; <br />
<br />
procedure CreatePascalThread;<br />
var<br />
ThreadData: PCdeclThreadFunc; <br />
begin <br />
New(ThreadData);<br />
// this is the desired cdecl thread function<br />
ThreadData^.Func := func; <br />
ThreadData^.Data := user_data; <br />
// this creates the Pascal thread<br />
BeginThread(@C2P_Translator, ThreadData );<br />
end;</Delphi><br />
<br />
<br />
* Initialize the FPC's threading system by creating a dummy thread. If you don't create any Pascal thread in your app, the thread system won't be initialized (and thus threadvars won't work and thus heap will not work correctly). <br />
<br />
<Delphi>type<br />
tc = class(tthread)<br />
procedure execute;override;<br />
end;<br />
<br />
procedure tc.execute;<br />
begin<br />
end;<br />
<br />
{ main program } <br />
begin<br />
{ initialise threading system }<br />
with tc.create(false) do<br />
begin<br />
waitfor;<br />
free;<br />
end;<br />
{ ... your code follows } <br />
end.</Delphi><br />
<br />
(After the threading system is initialized, the runtime may set the system variable "IsMultiThread" to true which is used by FPC routines to perform locks here and there. You should not set this variable manually.) <br />
<br />
<br />
* If for some reason this doesn't work for you, try this code in your external thread function:<br />
<br />
<Delphi>function ExternalThread(param: Pointer): LongInt; stdcall;<br />
var<br />
tm: TThreadManager;<br />
begin<br />
GetThreadManager(tm);<br />
tm.AllocateThreadVars;<br />
InitThread(1000000); // adjust inital stack size here<br />
<br />
{ do something threaded here ... }<br />
<br />
Result:=0;<br />
end;</Delphi><br />
<br />
<br />
=== Identifying external threads ===<br />
Sometimes you even don't know if you have to deal with external threads (e.g. if some C library makes a callback). This can help to analyse this: <br />
<br />
1. Ask the OS for the ID of the current thread at your application's start <br />
<br />
<Delphi>Win32: GetCurrentThreadID();<br />
Darwin: GetThreadID(); <br />
Linux: TThreadID(pthread_self);</Delphi><br />
<br />
2. Ask again for the ID of the current thread inside the thread function and compare this by the result of step 1.<br />
<br />
==See also==<br />
<br />
* [[Streaming components]]<br />
* [[Manager Worker Threads System]]<br />
* [[Example of multi-threaded application: array of threads]]<br />
* [[Parallel procedures]]<br />
<br />
[[Category:Tutorials]]</div>Mvampirehttps://wiki.freepascal.org/index.php?title=Example_of_multi-threaded_application:_array_of_threads&diff=44846Example of multi-threaded application: array of threads2010-09-10T19:41:12Z<p>Mvampire: /* 3. Rewrite main program. */</p>
<hr />
<div>Here I want to show an example how to create a lot of threads and wait while they will not finish their jobs (I don't need any synchronisation). I'm writing this tutorial because it was not obvious for me to write such a program after reading [[Multithreaded Application Tutorial]]. I was writing my application for Linux x86_64.<br />
<br />
Let's assume that we have the following loop:<br />
<delphi>var results: integer;<br />
File: text;<br />
begin<br />
<br />
...<br />
<br />
for i:=1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
...</delphi><br />
<br />
This loop runs procedure that "calculate" some 'result'. After this we write this array to a text file... Now we want to divide this job to many parallel threads.<br />
<br />
To do this, You need to perform the following steps:<br />
<br />
== 1. Manage memory. == <br />
Your threads can read any variables any time You need, but they shouldn't write any data to same variables (global) at the same time. So Your threads will not be able to write anything to one variable like 'result'. And they will not be able to write it to file. Moreover, I was interested to write my results to file in order (from 1 to n), and threads will not do this. So first step is to create global array of results (1 cell for 1 thread). If your procedure write something to many variables - you can create several arrays or array of record.<br />
<br />
I use a separate unit for global variables:<br />
<delphi>unit Global;<br />
{$mode objfpc}{$H+}<br />
interface<br />
uses Classes, SysUtils; <br />
<br />
var results: array [1..100] of threads; //We will create no more than 100 threads.<br />
<br />
implementation<br />
end.</delphi><br />
<br />
== 2. Add threads class. ==<br />
I also use a separate unit for it. So I need to describe 'property Terminated' as the public (to use it from the main program).<br />
<br />
<delphi>unit MyThreads;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils; <br />
<br />
Type<br />
TMyThread = class(TThread)<br />
private<br />
protected<br />
procedure Execute; override;<br />
public<br />
start,finish: integer; //I will initialise these fields before running the thread. So each thread will receive a part of task.<br />
property Terminated;<br />
Constructor Create(CreateSuspended : boolean);<br />
end;<br />
<br />
implementation<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
i: integer;<br />
begin<br />
i:=start;<br />
while (not Terminated) and (i<=finish) do<br />
begin<br />
MyProcedure(results[i]);<br />
inc(i);<br />
if i=finish+1 then Terminate;<br />
end;<br />
end;<br />
end.</delphi><br />
<br />
== 3. Rewrite main program. ==<br />
<br />
You need to add 'cthreads' to the main unit, not to unit with threads!<br />
<br />
Tips: in my Lazarus IDE I was not able to debug multi-threading applications if I don't use 'pthreads'. I have red that if you use 'cmem', the program works faster, but I strongly recommend you to check it for any particular case (my program hangs when I use 'cmem').<br />
<br />
<delphi>uses cthreads,<br />
// cmem,<br />
// pthreads,<br />
Classes, SysUtils, CustApp, MyThreads, Global<br />
<br />
var<br />
ThreadArray: array [1..100] of TMyThread; //We will create no more than 100 threads.<br />
i, number_of_threads: integer;<br />
begin<br />
<br />
number of threads:=100;<br />
<br />
while n div number_of_threads <1 do dec(number_of_threads); //If n<100 then we don't have enough tasks for 100 threads.<br />
<br />
for i:=1 to number_of_threads do begin<br />
ThreadArray[i]:= TMyThread.Create(True);<br />
ThreadArray[i].start:=(i-1)*(n div number_of_threads)+1;;<br />
ThreadArray[i].finish:=i*(n div number_of_threads);<br />
ThreadArray[i].Resume;<br />
end;<br />
<br />
for i:=1 to 10 do if not ThreadArray[i].Terminated then Sleep(10); //Waiting while threads will not finish their job.<br />
<br />
for i:=1 to number_of_threads*(n div number_of_threads) do Writeln(file,results[i]); //Writing results in order.<br />
//Or You can calculate a sum here and write only it.<br />
<br />
//Now we should finish part of the task that couldn't be divided to 100 threads.<br />
//For examle, if n=10050 then 10000 will be divided to 100 threads. <br />
//And last 50 (< 100) we should finish in non-parallel mode.<br />
//That's how I do this. <br />
//Instead, You can write something like: 'if i=number_of_threads then ThreadArray[i].finish:=n;' in the loop above.<br />
//In this case last thread will finish the job.<br />
<br />
if n mod number_of_threads<>0 then<br />
for i:=(n div number_of_threads) + 1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
Terminate;<br />
end;</delphi><br />
<br />
[[Category:Tutorials]]</div>Mvampirehttps://wiki.freepascal.org/index.php?title=Example_of_multi-threaded_application:_array_of_threads&diff=44726Example of multi-threaded application: array of threads2010-09-07T21:31:50Z<p>Mvampire: /* 3. Rewrite main program. */</p>
<hr />
<div>Here I want to show an example how to create a lot of threads and wait while they will not finish their jobs (I don't need any synchronisation). I'm writing this tutorial because it was not obvious for me to write such a program after reading Multithread Application Tutorial. I was writing my application for Linux x86_64.<br />
<br />
Let's assume that we have the following loop:<br />
<delphi><br />
var results: integer;<br />
File: text;<br />
begin<br />
<br />
...<br />
<br />
for i:=1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
...<br />
</delphi><br />
This loop runs procedure that "calculate" some 'result'. After this we write this array to a text file... Now we want to divide this job to many parallel threads.<br />
<br />
To do this, You need to perform the following steps:<br />
<br />
== 1. Manage memory. == <br />
Your threads can read any variables any time You need, but they shouldn't write any data to same variables (global) at the same time. So Your threads will not be able to write anything to one variable like 'result'. And they will not be able to write it to file. Moreover, I was interested to write my results to file in order (from 1 to n), and threads will not do this. So first step is to create global array of results (1 cell for 1 thread). If your procedure write something to many variables - you can create several arrays or array of record.<br />
<br />
I use a separate unit for global variables:<br />
<delphi><br />
unit Global;<br />
{$mode objfpc}{$H+}<br />
interface<br />
uses Classes, SysUtils; <br />
<br />
var results: array [1..100] of threads; //We will create no more than 100 threads.<br />
<br />
implementation<br />
end.<br />
</delphi><br />
<br />
== 2. Add threads class. ==<br />
I also use a separate unit for it. So I need to describe 'property Terminated' as the public (to use it from the main program).<br />
<delphi><br />
unit MyThreads;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils; <br />
<br />
Type<br />
TMyThread = class(TThread)<br />
private<br />
protected<br />
procedure Execute; override;<br />
public<br />
start,finish: integer; //I will initialise these fields before running the thread. So each thread will receive a part of task.<br />
property Terminated;<br />
Constructor Create(CreateSuspended : boolean);<br />
end;<br />
<br />
implementation<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
i: integer;<br />
begin<br />
i:=start;<br />
while (not Terminated) and (i<=finish) do<br />
begin<br />
MyProcedure(results[i]);<br />
inc(i);<br />
if i=finish+1 then Terminate;<br />
end;<br />
end;<br />
end. <br />
</delphi><br />
<br />
== 3. Rewrite main program. ==<br />
<br />
You need to add 'cthreads' to the main unit, not to unit with threads!<br />
<br />
Tips: in my Lazarus IDE I was not able to debug multi-threading applications if I don't use 'pthreads'. I have red that if you use 'cmem', the program works faster, but I strongly recommend you to check it for any particular case (my program hangs when I use 'cmem').<br />
<delphi><br />
uses cthreads,<br />
// cmem,<br />
// pthreads,<br />
Classes, SysUtils, CustApp, MyThreads, Global<br />
<br />
var<br />
ThreadArray: array [1..100] of TMyThread; //We will create no more than 100 threads.<br />
i, number_of_threads: integer;<br />
begin<br />
<br />
number of threads:=100;<br />
<br />
while n div number_of_threads <1 do dec(number_of_threads); //If n<100 then we don't have enough tasks for 100 threads.<br />
<br />
for i:=1 to number_of_threads do begin<br />
ThreadArray[i]:= TMyThread.Create(True);<br />
ThreadArray[i].start:=(i-1)*(n div number_of_threads)+1;;<br />
ThreadArray[i].finish:=i*(n div number_of_threads);<br />
ThreadArray[i].Resume;<br />
end;<br />
<br />
for i:=1 to 10 do if not ThreadArray[i].Terminated then Sleep(10); //Waiting while threads will not finish their job.<br />
<br />
for i:=1 to number_of_threads*(n div number_of_threads) do Writeln(file,results[i]); //Writing results in order.<br />
//Or You can calculate a sum here and write only it.<br />
<br />
//Now we should finish part of the tast that couldn't be divided to 100 threads.<br />
//For examle, if n=10050 then 10000 will be divided to 100 threads. And last 50 (< 100) we should finish in non-parallel mode<br />
//That's how I do this. Instead, You can write something like: 'if i=number_of_threads then ThreadArray[i].finish:=n;'<br />
//In this case last thread will finish the job.<br />
<br />
if n mod number_of_threads<>0 then<br />
for i:=(n div number_of_threads) + 1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
Terminate;<br />
end;<br />
</delphi></div>Mvampirehttps://wiki.freepascal.org/index.php?title=Example_of_multi-threaded_application:_array_of_threads&diff=44725Example of multi-threaded application: array of threads2010-09-07T21:30:34Z<p>Mvampire: /* 2. Add threads class. */</p>
<hr />
<div>Here I want to show an example how to create a lot of threads and wait while they will not finish their jobs (I don't need any synchronisation). I'm writing this tutorial because it was not obvious for me to write such a program after reading Multithread Application Tutorial. I was writing my application for Linux x86_64.<br />
<br />
Let's assume that we have the following loop:<br />
<delphi><br />
var results: integer;<br />
File: text;<br />
begin<br />
<br />
...<br />
<br />
for i:=1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
...<br />
</delphi><br />
This loop runs procedure that "calculate" some 'result'. After this we write this array to a text file... Now we want to divide this job to many parallel threads.<br />
<br />
To do this, You need to perform the following steps:<br />
<br />
== 1. Manage memory. == <br />
Your threads can read any variables any time You need, but they shouldn't write any data to same variables (global) at the same time. So Your threads will not be able to write anything to one variable like 'result'. And they will not be able to write it to file. Moreover, I was interested to write my results to file in order (from 1 to n), and threads will not do this. So first step is to create global array of results (1 cell for 1 thread). If your procedure write something to many variables - you can create several arrays or array of record.<br />
<br />
I use a separate unit for global variables:<br />
<delphi><br />
unit Global;<br />
{$mode objfpc}{$H+}<br />
interface<br />
uses Classes, SysUtils; <br />
<br />
var results: array [1..100] of threads; //We will create no more than 100 threads.<br />
<br />
implementation<br />
end.<br />
</delphi><br />
<br />
== 2. Add threads class. ==<br />
I also use a separate unit for it. So I need to describe 'property Terminated' as the public (to use it from the main program).<br />
<delphi><br />
unit MyThreads;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils; <br />
<br />
Type<br />
TMyThread = class(TThread)<br />
private<br />
protected<br />
procedure Execute; override;<br />
public<br />
start,finish: integer; //I will initialise these fields before running the thread. So each thread will receive a part of task.<br />
property Terminated;<br />
Constructor Create(CreateSuspended : boolean);<br />
end;<br />
<br />
implementation<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
i: integer;<br />
begin<br />
i:=start;<br />
while (not Terminated) and (i<=finish) do<br />
begin<br />
MyProcedure(results[i]);<br />
inc(i);<br />
if i=finish+1 then Terminate;<br />
end;<br />
end;<br />
end. <br />
</delphi><br />
<br />
== 3. Rewrite main program. ==<br />
<br />
You need to add 'cthreads' to the main unit, not to unit with threads!<br />
<br />
Tips: in my Lazarus IDE I was not able to debug multi-threading applications if I don't use 'pthreads'. I have red that if you use 'cmem', the program works faster, but I strongly recommend you to check it for any particular case (my program hangs when I use 'cmem').<br />
<delphi><br />
uses cthreads,<br />
// cmem,<br />
// pthreads,<br />
Classes, SysUtils, CustApp, MyThreads, Global<br />
<br />
var<br />
ThreadArray: array [1..100] of TMyThread; //We will create no more than 100 threads.<br />
i, number_of_threads: integer;<br />
begin<br />
<br />
number of threads:=100;<br />
<br />
while n div number_of_threads <1 do dec(number_of_threads); //If n<100 then we don't have enough tasks for 100 threads.<br />
<br />
for i:=1 to number_of_threads do begin<br />
ThreadArray[i]:= TMyThread.Create(True);<br />
ThreadArray[i].start:=(i-1)*(n div number_of_threads)+1;;<br />
ThreadArray[i].finish:=i*(n div number_of_threads);<br />
ThreadArray[i].Resume;<br />
end;<br />
<br />
for i:=1 to 10 do if not ThreadArray[i].Terminated then Sleep(10); //Waiting while threads will not finish their job.<br />
<br />
for i:=1 to n do Writeln(file,result[i]); //Writing results in order.<br />
//Or You can calculate a sum here and write only it.<br />
<br />
//Now we should finish part of the tast that couldn't be divided to 100 threads.<br />
//For examle, if n=10050 then 10000 will be divided to 100 threads. And last 50 (< 100) we should finish in non-parallel mode<br />
//That's how I do this. Instead, You can write something like: 'if i=number_of_threads then ThreadArray[i].finish:=n;'<br />
//In this case last thread will finish the job.<br />
<br />
if n mod number_of_threads<>0 then<br />
for i:=(n div number_of_threads) + 1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
Terminate;<br />
end;<br />
</delphi></div>Mvampirehttps://wiki.freepascal.org/index.php?title=Example_of_multi-threaded_application:_array_of_threads&diff=44724Example of multi-threaded application: array of threads2010-09-07T21:30:11Z<p>Mvampire: /* 1. Manage memory. */</p>
<hr />
<div>Here I want to show an example how to create a lot of threads and wait while they will not finish their jobs (I don't need any synchronisation). I'm writing this tutorial because it was not obvious for me to write such a program after reading Multithread Application Tutorial. I was writing my application for Linux x86_64.<br />
<br />
Let's assume that we have the following loop:<br />
<delphi><br />
var results: integer;<br />
File: text;<br />
begin<br />
<br />
...<br />
<br />
for i:=1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
...<br />
</delphi><br />
This loop runs procedure that "calculate" some 'result'. After this we write this array to a text file... Now we want to divide this job to many parallel threads.<br />
<br />
To do this, You need to perform the following steps:<br />
<br />
== 1. Manage memory. == <br />
Your threads can read any variables any time You need, but they shouldn't write any data to same variables (global) at the same time. So Your threads will not be able to write anything to one variable like 'result'. And they will not be able to write it to file. Moreover, I was interested to write my results to file in order (from 1 to n), and threads will not do this. So first step is to create global array of results (1 cell for 1 thread). If your procedure write something to many variables - you can create several arrays or array of record.<br />
<br />
I use a separate unit for global variables:<br />
<delphi><br />
unit Global;<br />
{$mode objfpc}{$H+}<br />
interface<br />
uses Classes, SysUtils; <br />
<br />
var results: array [1..100] of threads; //We will create no more than 100 threads.<br />
<br />
implementation<br />
end.<br />
</delphi><br />
<br />
== 2. Add threads class. ==<br />
I also use a separate unit for it. So I need to describe 'property Terminated' as the public (to use it from the main program).<br />
<delphi><br />
unit MyThreads;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils; <br />
<br />
Type<br />
TMyThread = class(TThread)<br />
private<br />
protected<br />
procedure Execute; override;<br />
public<br />
start,finish: integer; //I will initialise these fields before running the thread. So each thread will receive a part of task.<br />
property Terminated;<br />
Constructor Create(CreateSuspended : boolean);<br />
end;<br />
<br />
implementation<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
i: integer;<br />
begin<br />
i:=start;<br />
while (not Terminated) and (i<=finish) do<br />
begin<br />
MyProcedure(result[i]);<br />
inc(i);<br />
if i=finish+1 then Terminate;<br />
end;<br />
end;<br />
end. <br />
</delphi><br />
== 3. Rewrite main program. ==<br />
<br />
You need to add 'cthreads' to the main unit, not to unit with threads!<br />
<br />
Tips: in my Lazarus IDE I was not able to debug multi-threading applications if I don't use 'pthreads'. I have red that if you use 'cmem', the program works faster, but I strongly recommend you to check it for any particular case (my program hangs when I use 'cmem').<br />
<delphi><br />
uses cthreads,<br />
// cmem,<br />
// pthreads,<br />
Classes, SysUtils, CustApp, MyThreads, Global<br />
<br />
var<br />
ThreadArray: array [1..100] of TMyThread; //We will create no more than 100 threads.<br />
i, number_of_threads: integer;<br />
begin<br />
<br />
number of threads:=100;<br />
<br />
while n div number_of_threads <1 do dec(number_of_threads); //If n<100 then we don't have enough tasks for 100 threads.<br />
<br />
for i:=1 to number_of_threads do begin<br />
ThreadArray[i]:= TMyThread.Create(True);<br />
ThreadArray[i].start:=(i-1)*(n div number_of_threads)+1;;<br />
ThreadArray[i].finish:=i*(n div number_of_threads);<br />
ThreadArray[i].Resume;<br />
end;<br />
<br />
for i:=1 to 10 do if not ThreadArray[i].Terminated then Sleep(10); //Waiting while threads will not finish their job.<br />
<br />
for i:=1 to n do Writeln(file,result[i]); //Writing results in order.<br />
//Or You can calculate a sum here and write only it.<br />
<br />
//Now we should finish part of the tast that couldn't be divided to 100 threads.<br />
//For examle, if n=10050 then 10000 will be divided to 100 threads. And last 50 (< 100) we should finish in non-parallel mode<br />
//That's how I do this. Instead, You can write something like: 'if i=number_of_threads then ThreadArray[i].finish:=n;'<br />
//In this case last thread will finish the job.<br />
<br />
if n mod number_of_threads<>0 then<br />
for i:=(n div number_of_threads) + 1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
Terminate;<br />
end;<br />
</delphi></div>Mvampirehttps://wiki.freepascal.org/index.php?title=Example_of_multi-threaded_application:_array_of_threads&diff=44723Example of multi-threaded application: array of threads2010-09-07T21:29:15Z<p>Mvampire: /* 3. Rewrite main program. */</p>
<hr />
<div>Here I want to show an example how to create a lot of threads and wait while they will not finish their jobs (I don't need any synchronisation). I'm writing this tutorial because it was not obvious for me to write such a program after reading Multithread Application Tutorial. I was writing my application for Linux x86_64.<br />
<br />
Let's assume that we have the following loop:<br />
<delphi><br />
var results: integer;<br />
File: text;<br />
begin<br />
<br />
...<br />
<br />
for i:=1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
...<br />
</delphi><br />
This loop runs procedure that "calculate" some 'result'. After this we write this array to a text file... Now we want to divide this job to many parallel threads.<br />
<br />
To do this, You need to perform the following steps:<br />
<br />
== 1. Manage memory. == <br />
Your threads can read any variables any time You need, but they shouldn't write any data to same variables (global) at the same time. So Your threads will not be able to write anything to one variable like 'result'. And they will not be able to write it to file. Moreover, I was interested to write my results to file in order (from 1 to n), and threads will not do this. So first step is to create global array of results (1 cell for 1 thread). If your procedure write something to many variables - you can create several arrays or array of record.<br />
<br />
I use a separate unit for global variables:<br />
<delphi><br />
unit Global;<br />
{$mode objfpc}{$H+}<br />
interface<br />
uses Classes, SysUtils; <br />
<br />
var result: array [1..100] of threads; //We will create no more than 100 threads.<br />
<br />
implementation<br />
end.<br />
</delphi><br />
<br />
== 2. Add threads class. ==<br />
I also use a separate unit for it. So I need to describe 'property Terminated' as the public (to use it from the main program).<br />
<delphi><br />
unit MyThreads;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils; <br />
<br />
Type<br />
TMyThread = class(TThread)<br />
private<br />
protected<br />
procedure Execute; override;<br />
public<br />
start,finish: integer; //I will initialise these fields before running the thread. So each thread will receive a part of task.<br />
property Terminated;<br />
Constructor Create(CreateSuspended : boolean);<br />
end;<br />
<br />
implementation<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
i: integer;<br />
begin<br />
i:=start;<br />
while (not Terminated) and (i<=finish) do<br />
begin<br />
MyProcedure(result[i]);<br />
inc(i);<br />
if i=finish+1 then Terminate;<br />
end;<br />
end;<br />
end. <br />
</delphi><br />
== 3. Rewrite main program. ==<br />
<br />
You need to add 'cthreads' to the main unit, not to unit with threads!<br />
<br />
Tips: in my Lazarus IDE I was not able to debug multi-threading applications if I don't use 'pthreads'. I have red that if you use 'cmem', the program works faster, but I strongly recommend you to check it for any particular case (my program hangs when I use 'cmem').<br />
<delphi><br />
uses cthreads,<br />
// cmem,<br />
// pthreads,<br />
Classes, SysUtils, CustApp, MyThreads, Global<br />
<br />
var<br />
ThreadArray: array [1..100] of TMyThread; //We will create no more than 100 threads.<br />
i, number_of_threads: integer;<br />
begin<br />
<br />
number of threads:=100;<br />
<br />
while n div number_of_threads <1 do dec(number_of_threads); //If n<100 then we don't have enough tasks for 100 threads.<br />
<br />
for i:=1 to number_of_threads do begin<br />
ThreadArray[i]:= TMyThread.Create(True);<br />
ThreadArray[i].start:=(i-1)*(n div number_of_threads)+1;;<br />
ThreadArray[i].finish:=i*(n div number_of_threads);<br />
ThreadArray[i].Resume;<br />
end;<br />
<br />
for i:=1 to 10 do if not ThreadArray[i].Terminated then Sleep(10); //Waiting while threads will not finish their job.<br />
<br />
for i:=1 to n do Writeln(file,result[i]); //Writing results in order.<br />
//Or You can calculate a sum here and write only it.<br />
<br />
//Now we should finish part of the tast that couldn't be divided to 100 threads.<br />
//For examle, if n=10050 then 10000 will be divided to 100 threads. And last 50 (< 100) we should finish in non-parallel mode<br />
//That's how I do this. Instead, You can write something like: 'if i=number_of_threads then ThreadArray[i].finish:=n;'<br />
//In this case last thread will finish the job.<br />
<br />
if n mod number_of_threads<>0 then<br />
for i:=(n div number_of_threads) + 1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
Terminate;<br />
end;<br />
</delphi></div>Mvampirehttps://wiki.freepascal.org/index.php?title=Example_of_multi-threaded_application:_array_of_threads&diff=44722Example of multi-threaded application: array of threads2010-09-07T21:27:08Z<p>Mvampire: </p>
<hr />
<div>Here I want to show an example how to create a lot of threads and wait while they will not finish their jobs (I don't need any synchronisation). I'm writing this tutorial because it was not obvious for me to write such a program after reading Multithread Application Tutorial. I was writing my application for Linux x86_64.<br />
<br />
Let's assume that we have the following loop:<br />
<delphi><br />
var results: integer;<br />
File: text;<br />
begin<br />
<br />
...<br />
<br />
for i:=1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
...<br />
</delphi><br />
This loop runs procedure that "calculate" some 'result'. After this we write this array to a text file... Now we want to divide this job to many parallel threads.<br />
<br />
To do this, You need to perform the following steps:<br />
<br />
== 1. Manage memory. == <br />
Your threads can read any variables any time You need, but they shouldn't write any data to same variables (global) at the same time. So Your threads will not be able to write anything to one variable like 'result'. And they will not be able to write it to file. Moreover, I was interested to write my results to file in order (from 1 to n), and threads will not do this. So first step is to create global array of results (1 cell for 1 thread). If your procedure write something to many variables - you can create several arrays or array of record.<br />
<br />
I use a separate unit for global variables:<br />
<delphi><br />
unit Global;<br />
{$mode objfpc}{$H+}<br />
interface<br />
uses Classes, SysUtils; <br />
<br />
var result: array [1..100] of threads; //We will create no more than 100 threads.<br />
<br />
implementation<br />
end.<br />
</delphi><br />
<br />
== 2. Add threads class. ==<br />
I also use a separate unit for it. So I need to describe 'property Terminated' as the public (to use it from the main program).<br />
<delphi><br />
unit MyThreads;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils; <br />
<br />
Type<br />
TMyThread = class(TThread)<br />
private<br />
protected<br />
procedure Execute; override;<br />
public<br />
start,finish: integer; //I will initialise these fields before running the thread. So each thread will receive a part of task.<br />
property Terminated;<br />
Constructor Create(CreateSuspended : boolean);<br />
end;<br />
<br />
implementation<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
i: integer;<br />
begin<br />
i:=start;<br />
while (not Terminated) and (i<=finish) do<br />
begin<br />
MyProcedure(result[i]);<br />
inc(i);<br />
if i=finish+1 then Terminate;<br />
end;<br />
end;<br />
end. <br />
</delphi><br />
== 3. Rewrite main program. ==<br />
<br />
You need to add 'cthreads' to the main unit, not to unit with threads!<br />
<br />
Tips: in my Lazarus IDE I was not able to debug multi-threading applications if I don't use 'pthreads'. I have red that if you use 'cmem', the program works faster, but I strongly recommend you to check it for any particular case (my program hangs when I use 'cmem').<br />
<delphi><br />
uses cthreads,<br />
// cmem,<br />
// pthreads,<br />
Classes, SysUtils, CustApp, MyThreads, Global<br />
<br />
var<br />
ThreadArray: array [1..100] of TMyThread; //We will create no more than 100 threads.<br />
i, number_of_threads: integer;<br />
begin<br />
<br />
number of threads:=100;<br />
<br />
while n div number_of_threads <1 do dec(number_of_threads); //If n<100 then we don't have enough tasks for 100 threads.<br />
<br />
for i:=1 to number_of_threads do begin<br />
ThreadArray[i]:= TMyThread.Create(True);<br />
ThreadArray[i].start:=(i-1)*(n div number_of_threads)+1;;<br />
ThreadArray[i].finish:=i*(n div number_of_threads);<br />
ThreadArray[i].Resume;<br />
end;<br />
<br />
for i:=1 to 10 do if not ThreadArray[i].Terminated then Sleep(10); //Waiting while threads will not finish their job.<br />
<br />
for i:=1 to n do Writeln(file,result[i]); //Writing results in order.<br />
<br />
//Now we should finish part of the tast that couldn't be divided to 100 threads.<br />
//For examle, if n=10050 then 10000 will be divided to 100 threads. And last 50 (< 100) we should finish in non-parallel mode<br />
//That's how I do this. Instead, You can write something like: 'if i=number_of_threads then ThreadArray[i].finish:=n;'<br />
//In this case last thread will finish the job.<br />
<br />
if n mod number_of_threads<>0 then<br />
for i:=(n div number_of_threads) + 1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
Terminate;<br />
end;<br />
</delphi></div>Mvampirehttps://wiki.freepascal.org/index.php?title=Example_of_multi-threaded_application:_array_of_threads&diff=44721Example of multi-threaded application: array of threads2010-09-07T21:25:54Z<p>Mvampire: /* 1. Manage memory. */</p>
<hr />
<div>Here I want to show an example how to create many threads and wait while they will not finish their jobs (I don't need any synchronisation). I'm writing this tutorial because it was not obvious for me to write such a program after reading Multithread Application Tutorial. I was writing my application for Linux x86_64.<br />
<br />
Let's assume that we have the following loop:<br />
<delphi><br />
var results: integer;<br />
File: text;<br />
begin<br />
<br />
...<br />
<br />
for i:=1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
...<br />
</delphi><br />
This loop runs procedure that "calculate" some 'result'. After this we write this array to a text file... Now we want to divide this job to many parallel threads.<br />
<br />
To do this, You need to perform the following steps:<br />
<br />
== 1. Manage memory. == <br />
Your threads can read any variables any time You need, but they shouldn't write any data to same variables (global) at the same time. So Your threads will not be able to write anything to one variable like 'result'. And they will not be able to write it to file. Moreover, I was interested to write my results to file in order (from 1 to n), and threads will not do this. So first step is to create global array of results (1 cell for 1 thread). If your procedure write something to many variables - you can create several arrays or array of record.<br />
<br />
I use a separate unit for global variables:<br />
<delphi><br />
unit Global;<br />
{$mode objfpc}{$H+}<br />
interface<br />
uses Classes, SysUtils; <br />
<br />
var result: array [1..100] of threads; //We will create no more than 100 threads.<br />
<br />
implementation<br />
end.<br />
</delphi><br />
<br />
== 2. Add threads class. ==<br />
I also use a separate unit for it. So I need to describe 'property Terminated' as the public (to use it from the main program).<br />
<delphi><br />
unit MyThreads;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils; <br />
<br />
Type<br />
TMyThread = class(TThread)<br />
private<br />
protected<br />
procedure Execute; override;<br />
public<br />
start,finish: integer; //I will initialise these fields before running the thread. So each thread will receive a part of task.<br />
property Terminated;<br />
Constructor Create(CreateSuspended : boolean);<br />
end;<br />
<br />
implementation<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
i: integer;<br />
begin<br />
i:=start;<br />
while (not Terminated) and (i<=finish) do<br />
begin<br />
MyProcedure(result[i]);<br />
inc(i);<br />
if i=finish+1 then Terminate;<br />
end;<br />
end;<br />
end. <br />
</delphi><br />
== 3. Rewrite main program. ==<br />
<br />
You need to add 'cthreads' to the main unit, not to unit with threads!<br />
<br />
Tips: in my Lazarus IDE I was not able to debug multi-threading applications if I don't use 'pthreads'. I have red that if you use 'cmem', the program works faster, but I strongly recommend you to check it for any particular case (my program hangs when I use 'cmem').<br />
<delphi><br />
uses cthreads,<br />
// cmem,<br />
// pthreads,<br />
Classes, SysUtils, CustApp, MyThreads, Global<br />
<br />
var<br />
ThreadArray: array [1..100] of TMyThread; //We will create no more than 100 threads.<br />
i, number_of_threads: integer;<br />
begin<br />
<br />
number of threads:=100;<br />
<br />
while n div number_of_threads <1 do dec(number_of_threads); //If n<100 then we don't have enough tasks for 100 threads.<br />
<br />
for i:=1 to number_of_threads do begin<br />
ThreadArray[i]:= TMyThread.Create(True);<br />
ThreadArray[i].start:=(i-1)*(n div number_of_threads)+1;;<br />
ThreadArray[i].finish:=i*(n div number_of_threads);<br />
ThreadArray[i].Resume;<br />
end;<br />
<br />
for i:=1 to 10 do if not ThreadArray[i].Terminated then Sleep(10); //Waiting while threads will not finish their job.<br />
<br />
for i:=1 to n do Writeln(file,result[i]); //Writing results in order.<br />
<br />
//Now we should finish part of the tast that couldn't be divided to 100 threads.<br />
//For examle, if n=10050 then 10000 will be divided to 100 threads. And last 50 (< 100) we should finish in non-parallel mode<br />
//That's how I do this. Instead, You can write something like: 'if i=number_of_threads then ThreadArray[i].finish:=n;'<br />
//In this case last thread will finish the job.<br />
<br />
if n mod number_of_threads<>0 then<br />
for i:=(n div number_of_threads) + 1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
Terminate;<br />
end;<br />
</delphi></div>Mvampirehttps://wiki.freepascal.org/index.php?title=Example_of_multi-threaded_application:_array_of_threads&diff=44720Example of multi-threaded application: array of threads2010-09-07T21:25:08Z<p>Mvampire: </p>
<hr />
<div>Here I want to show an example how to create many threads and wait while they will not finish their jobs (I don't need any synchronisation). I'm writing this tutorial because it was not obvious for me to write such a program after reading Multithread Application Tutorial. I was writing my application for Linux x86_64.<br />
<br />
Let's assume that we have the following loop:<br />
<delphi><br />
var results: integer;<br />
File: text;<br />
begin<br />
<br />
...<br />
<br />
for i:=1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
...<br />
</delphi><br />
This loop runs procedure that "calculate" some 'result'. After this we write this array to a text file... Now we want to divide this job to many parallel threads.<br />
<br />
To do this, You need to perform the following steps:<br />
<br />
== 1. Manage memory. == <br />
Your threads can read any variables any time You need, but they shouldn't write any data to same variables (global) at the same time. So Your threads will not be able to write anything to one variable like 'result'. And they will not be able to write it to file. Moreover, I was interested to write my results to file in order (from 1 to n), and threads will not do this. So first step is to create global array of results (1 cell for 1 thread). If your procedure write something to many variables - you can create several arrays or array of record.<br />
<br />
I use a separate unit for global variables:<br />
<delphi><br />
unit Global;<br />
{$mode objfpc}{$H+}<br />
interface<br />
uses Classes, SysUtils; <br />
<br />
var result: array [1..100] of threads; //We will create not more than 100 threads.<br />
<br />
implementation<br />
end.<br />
</delphi><br />
== 2. Add threads class. ==<br />
I also use a separate unit for it. So I need to describe 'property Terminated' as the public (to use it from the main program).<br />
<delphi><br />
unit MyThreads;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils; <br />
<br />
Type<br />
TMyThread = class(TThread)<br />
private<br />
protected<br />
procedure Execute; override;<br />
public<br />
start,finish: integer; //I will initialise these fields before running the thread. So each thread will receive a part of task.<br />
property Terminated;<br />
Constructor Create(CreateSuspended : boolean);<br />
end;<br />
<br />
implementation<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
i: integer;<br />
begin<br />
i:=start;<br />
while (not Terminated) and (i<=finish) do<br />
begin<br />
MyProcedure(result[i]);<br />
inc(i);<br />
if i=finish+1 then Terminate;<br />
end;<br />
end;<br />
end. <br />
</delphi><br />
== 3. Rewrite main program. ==<br />
<br />
You need to add 'cthreads' to the main unit, not to unit with threads!<br />
<br />
Tips: in my Lazarus IDE I was not able to debug multi-threading applications if I don't use 'pthreads'. I have red that if you use 'cmem', the program works faster, but I strongly recommend you to check it for any particular case (my program hangs when I use 'cmem').<br />
<delphi><br />
uses cthreads,<br />
// cmem,<br />
// pthreads,<br />
Classes, SysUtils, CustApp, MyThreads, Global<br />
<br />
var<br />
ThreadArray: array [1..100] of TMyThread; //We will create no more than 100 threads.<br />
i, number_of_threads: integer;<br />
begin<br />
<br />
number of threads:=100;<br />
<br />
while n div number_of_threads <1 do dec(number_of_threads); //If n<100 then we don't have enough tasks for 100 threads.<br />
<br />
for i:=1 to number_of_threads do begin<br />
ThreadArray[i]:= TMyThread.Create(True);<br />
ThreadArray[i].start:=(i-1)*(n div number_of_threads)+1;;<br />
ThreadArray[i].finish:=i*(n div number_of_threads);<br />
ThreadArray[i].Resume;<br />
end;<br />
<br />
for i:=1 to 10 do if not ThreadArray[i].Terminated then Sleep(10); //Waiting while threads will not finish their job.<br />
<br />
for i:=1 to n do Writeln(file,result[i]); //Writing results in order.<br />
<br />
//Now we should finish part of the tast that couldn't be divided to 100 threads.<br />
//For examle, if n=10050 then 10000 will be divided to 100 threads. And last 50 (< 100) we should finish in non-parallel mode<br />
//That's how I do this. Instead, You can write something like: 'if i=number_of_threads then ThreadArray[i].finish:=n;'<br />
//In this case last thread will finish the job.<br />
<br />
if n mod number_of_threads<>0 then<br />
for i:=(n div number_of_threads) + 1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
Terminate;<br />
end;<br />
</delphi></div>Mvampirehttps://wiki.freepascal.org/index.php?title=Example_of_multi-threaded_application:_array_of_threads&diff=44719Example of multi-threaded application: array of threads2010-09-07T21:21:41Z<p>Mvampire: /* 1. Manage memory. */</p>
<hr />
<div>Here I want to show an example how to create many threads and wait while they will not finish their jobs. I'm writing this tutorial because it was not obvious for me to write such a program after reading Multithread Application Tutorial. I was writing my application for Linux x86_64.<br />
<br />
Let's assume that we have the following loop:<br />
<delphi><br />
var results: integer;<br />
File: text;<br />
begin<br />
<br />
...<br />
<br />
for i:=1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
...<br />
</delphi><br />
This loop runs procedure that "calculate" some 'result'. After this we write this array to a text file... Now we want to divide this job to many parallel threads.<br />
<br />
To do this, You need to perform the following steps:<br />
<br />
== 1. Manage memory. == <br />
Your threads can read any variables any time You need, but they shouldn't write any data to same variables (global) at the same time. So Your threads will not be able to write anything to one variable like 'result'. And they will not be able to write it to file. Moreover, I was interested to write my results to file in order (from 1 to n), and threads will not do this. So first step is to create global array of results (1 cell for 1 thread). If your procedure write something to many variables - you can create several arrays or array of record.<br />
<br />
I use a separate unit for global variables:<br />
<delphi><br />
unit Global;<br />
{$mode objfpc}{$H+}<br />
interface<br />
uses Classes, SysUtils; <br />
<br />
var result: array [1..100] of threads; //We will create not more than 100 threads.<br />
<br />
implementation<br />
end.<br />
</delphi><br />
== 2. Add threads class. ==<br />
I also use a separate unit for it. So I need to describe 'property Terminated' as the public (to use it from the main program).<br />
<delphi><br />
unit MyThreads;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils; <br />
<br />
Type<br />
TMyThread = class(TThread)<br />
private<br />
protected<br />
procedure Execute; override;<br />
public<br />
start,finish: integer; //I will initialise these fields before running the thread. So each thread will receive a part of task.<br />
property Terminated;<br />
Constructor Create(CreateSuspended : boolean);<br />
end;<br />
<br />
implementation<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
i: integer;<br />
begin<br />
i:=start;<br />
while (not Terminated) and (i<=finish) do<br />
begin<br />
MyProcedure(result[i]);<br />
inc(i);<br />
if i=finish+1 then Terminate;<br />
end;<br />
end;<br />
end. <br />
</delphi><br />
== 3. Rewrite main program. ==<br />
<br />
You need to add 'cthreads' to the main unit, not to unit with threads!<br />
<br />
Tips: in my Lazarus IDE I was not able to debug multi-threading applications if I don't use 'pthreads'. I have red that if you use 'cmem', the program works faster, but I strongly recommend you to check it for any particular case (my program hangs when I use 'cmem').<br />
<delphi><br />
uses cthreads,<br />
// cmem,<br />
// pthreads,<br />
Classes, SysUtils, CustApp, MyThreads, Global<br />
<br />
var<br />
ThreadArray: array [1..100] of TMyThread; //We will create no more than 100 threads.<br />
i, number_of_threads: integer;<br />
begin<br />
<br />
number of threads:=100;<br />
<br />
while n div number_of_threads <1 do dec(number_of_threads); //If n<100 then we don't have enough tasks for 100 threads.<br />
<br />
for i:=1 to number_of_threads do begin<br />
ThreadArray[i]:= TMyThread.Create(True);<br />
ThreadArray[i].start:=(i-1)*(n div number_of_threads)+1;;<br />
ThreadArray[i].finish:=i*(n div number_of_threads);<br />
ThreadArray[i].Resume;<br />
end;<br />
<br />
for i:=1 to 10 do if not ThreadArray[i].Terminated then Sleep(10); //Waiting while threads will not finish their job.<br />
<br />
for i:=1 to n do Writeln(file,result[i]); //Writing results in order.<br />
<br />
//Now we should finish part of the tast that couldn't be divided to 100 threads.<br />
//For examle, if n=10050 then 10000 will be divided to 100 threads. And last 50 (< 100) we should finish in non-parallel mode<br />
//That's how I do this. Instead, You can write something like: 'if i=number_of_threads then ThreadArray[i].finish:=n;'<br />
//In this case last thread will finish the job.<br />
<br />
if n mod number_of_threads<>0 then<br />
for i:=(n div number_of_threads) + 1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
Terminate;<br />
end;<br />
</delphi></div>Mvampirehttps://wiki.freepascal.org/index.php?title=Example_of_multi-threaded_application:_array_of_threads&diff=44718Example of multi-threaded application: array of threads2010-09-07T21:20:49Z<p>Mvampire: </p>
<hr />
<div>Here I want to show an example how to create many threads and wait while they will not finish their jobs. I'm writing this tutorial because it was not obvious for me to write such a program after reading Multithread Application Tutorial. I was writing my application for Linux x86_64.<br />
<br />
Let's assume that we have the following loop:<br />
<delphi><br />
var results: integer;<br />
File: text;<br />
begin<br />
<br />
...<br />
<br />
for i:=1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
...<br />
</delphi><br />
This loop runs procedure that "calculate" some 'result'. After this we write this array to a text file... Now we want to divide this job to many parallel threads.<br />
<br />
To do this, You need to perform the following steps:<br />
<br />
== 1. Manage memory. == <br />
Your threads can read any variables any time You need, but they shouldn't write any data to same variables (global) at the same time. So Your threads will not be able to write anything to one variable like 'result'. And they will not be able to write it to file. Moreover, I was interested to write my results to file in order (from 1 to n), and threads will not do this. So first step is to create global array of results (1 cell for 1 thread). If your procedure write something to many variables - you can create several arrays or array of record.<br />
<br />
I use a separate unit for global variables:<br />
<delphi><br />
unit Global;<br />
{$mode objfpc}{$H+}<br />
interface<br />
uses Classes, SysUtils; <br />
<br />
var result: array [1..100] of threads; //We will create not more than 100 threads.<br />
<br />
implementation<br />
end.<br />
</delphi><br />
2. Add threads class. I also use a separate unit for it. So I need to describe 'property Terminated' as the public (to use it from the main program).<br />
<delphi><br />
unit MyThreads;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils; <br />
<br />
Type<br />
TMyThread = class(TThread)<br />
private<br />
protected<br />
procedure Execute; override;<br />
public<br />
start,finish: integer; //I will initialise these fields before running the thread. So each thread will receive a part of task.<br />
property Terminated;<br />
Constructor Create(CreateSuspended : boolean);<br />
end;<br />
<br />
implementation<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
i: integer;<br />
begin<br />
i:=start;<br />
while (not Terminated) and (i<=finish) do<br />
begin<br />
MyProcedure(result[i]);<br />
inc(i);<br />
if i=finish+1 then Terminate;<br />
end;<br />
end;<br />
end. <br />
</delphi><br />
3. Rewrite main program.<br />
<br />
You need to add 'cthreads' to the main unit, not to unit with threads!<br />
<br />
Tips: in my Lazarus IDE I was not able to debug multi-threading applications if I don't use 'pthreads'. I have red that if you use 'cmem', the program works faster, but I strongly recommend you to check it for any particular case (my program hangs when I use 'cmem').<br />
<delphi><br />
uses cthreads,<br />
// cmem,<br />
// pthreads,<br />
Classes, SysUtils, CustApp, MyThreads, Global<br />
<br />
var<br />
ThreadArray: array [1..100] of TMyThread; //We will create no more than 100 threads.<br />
i, number_of_threads: integer;<br />
begin<br />
<br />
number of threads:=100;<br />
<br />
while n div number_of_threads <1 do dec(number_of_threads); //If n<100 then we don't have enough tasks for 100 threads.<br />
<br />
for i:=1 to number_of_threads do begin<br />
ThreadArray[i]:= TMyThread.Create(True);<br />
ThreadArray[i].start:=(i-1)*(n div number_of_threads)+1;;<br />
ThreadArray[i].finish:=i*(n div number_of_threads);<br />
ThreadArray[i].Resume;<br />
end;<br />
<br />
for i:=1 to 10 do if not ThreadArray[i].Terminated then Sleep(10); //Waiting while threads will not finish their job.<br />
<br />
for i:=1 to n do Writeln(file,result[i]); //Writing results in order.<br />
<br />
//Now we should finish part of the tast that couldn't be divided to 100 threads.<br />
//For examle, if n=10050 then 10000 will be divided to 100 threads. And last 50 (< 100) we should finish in non-parallel mode<br />
//That's how I do this. Instead, You can write something like: 'if i=number_of_threads then ThreadArray[i].finish:=n;'<br />
//In this case last thread will finish the job.<br />
<br />
if n mod number_of_threads<>0 then<br />
for i:=(n div number_of_threads) + 1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
Terminate;<br />
end;<br />
</delphi></div>Mvampirehttps://wiki.freepascal.org/index.php?title=Multithreaded_Application_Tutorial&diff=44717Multithreaded Application Tutorial2010-09-07T21:19:36Z<p>Mvampire: /* See also */</p>
<hr />
<div>{{Multithreaded Application Tutorial}}<br />
<br />
== Overview ==<br />
This page will try to explain how to write and debug a multi-threaded application with Free Pascal and Lazarus. A multi-threaded application is one that creates two or more threads of execution that work at the same time. If you are new to multi-threading, please read the paragraph "Do you need multi-threading?" to determine whether it is really required; this may save you many headaches.<br />
<br />
One of the threads is called the Main Thread. The Main Thread is the one that is created by the Operating System once our application starts. The Main Thread '''must be''' the only thread that updates the components that interfaces with the user: otherwise, the application may hang.<br />
<br />
The main idea is that the application can do some processing in background in a second thread while the user can continue working using the main thread.<br />
<br />
Another use of threads is just to have a better responding application. If you create an application, and when the user presses a button the application starts processing a big job... and while processing, the screen stops responding, and gives the user the impression that the application is frozen, a poor or misleading impression will be created. If the big job runs in a second thread, the application keeps responding (almost) as if it were idle. In this case it is a good idea, before starting the thread, to disable the buttons of the form to avoid the user starting more than one thread for the job.<br />
<br />
Another use of multi-threading may be a server application that is able to respond to many clients at the same time.<br />
<br />
== Do you need multi-threading? ==<br />
<br />
If you are new to multi-threading and you only want to make your application more responsive while your application performs moderately long-running tasks, then multi-threading may be more than is required.<br />
Multi-threaded applications are always more difficult to debug and they are often much more complex; in many cases you don't need multi-threading. A single thread is enough. If you can split up the time-consuming task into several smaller chunks, then instead you should use '''Application.ProcessMessages'''. This method allows the LCL to handle all waiting messages and returns.<br />
The central idea is to call Application.ProcessMessages at regular intervals during the execution of a long-running task to determine whether the user has clicked on something, or a progress indicator must be repainted, and so on.<br />
<br />
For example: Reading a big file and process it. <br />
See examples/multithreading/singlethreadingexample1.lpi.<br />
<br />
Multi-threading is only needed for<br />
* blocking handles, like network communications<br />
* using multiple processors simultaneously (SMP)<br />
* algorithms and library calls that must be called through an API an as such cannot be split up into smaller parts.<br />
<br />
== The TThread Class ==<br />
<br />
The following example can be found in the examples/multithreading/ directory.<br />
<br />
To create a multi-threaded application, the easiest way is to use the TThread Class. This class permits the creation of an additional thread (alongside the main thread) in a simple way. Normally you are required to override only 2 methods: the Create constructor, and the Execute method.<br />
<br />
In the constructor, you will prepare the thread to run. You will set the initial values of the variables or properties you need. The original constructor of TThread requires a parameter called Suspended. As you might expect, setting Suspended = True will prevent the thread starting automatically after the creation. If Suspended = False, the thread will start running just after the creation. If the thread is created suspended, then it will run only after the Resume method is called.<br />
<br />
As of FPC version 2.0.1 and later, TThread.Create also has an implicit parameter for Stack Size. You can now change the default stack size of each thread you create if you need it. Deep procedure call recursions in a thread are a good example. If you don't specify the stack size parameter, a default OS stack size is used.<br />
<br />
In the overridden Execute method you will write the code that will run on the thread.<br />
<br />
The TThread class has one important property: <br />
Terminated : boolean;<br />
<br />
If the thread has a loop (and this is typical), the loop should be exited when Terminated is true (it is false by default). Within each pass, the value of Terminated must be checked, and if it is true then the loop should be exited as quickly as is appropriate, after any necessary cleanup. Bear in mind that the Terminate method does not do anything by default: the .Execute method must explicitly implement support for it to quit its job.<br />
<br />
As we explained earlier, the thread should not interact with the visible components. Updates to visible components must be made within the context of the main thread. To do this, a TThread method called Synchronize exists. Synchronize requires a method (that takes no parameters) as an argument. When you call that method through Synchronize(@MyMethod), the thread execution will be paused, the code of MyMethod will run in the main thread, and then the thread execution will be resumed. The exact working of Synchronize depends on the platform, but basically it does this: it posts a message onto the main message queue and goes to sleep. Eventually the main thread processes the message and calls MyMethod. This way MyMethod is called without context, that means not during a mouse down event or during paint event, but after. After the main thread executed MyMethod, it wakes the sleeping Thread and processes the next message. The Thread then continues.<br />
<br />
There is another important property of TThread: FreeOnTerminate. If this property is true, the thread object is automatically freed when the thread execution (.Execute method) stops. Otherwise the application will need to free it manually.<br />
<br />
Example:<br />
<br />
<delphi> Type<br />
TMyThread = class(TThread)<br />
private<br />
fStatusText : string;<br />
procedure ShowStatus;<br />
protected<br />
procedure Execute; override;<br />
public<br />
Constructor Create(CreateSuspended : boolean);<br />
end;<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.ShowStatus;<br />
// this method is executed by the mainthread and can therefore access all GUI elements.<br />
begin<br />
Form1.Caption := fStatusText;<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
newStatus : string;<br />
begin<br />
fStatusText := 'TMyThread Starting...';<br />
Synchronize(@Showstatus);<br />
fStatusText := 'TMyThread Running...';<br />
while (not Terminated) and ([any condition required]) do<br />
begin<br />
...<br />
[here goes the code of the main thread loop]<br />
...<br />
if NewStatus <> fStatusText then<br />
begin<br />
fStatusText := newStatus;<br />
Synchronize(@Showstatus);<br />
end;<br />
end;<br />
end;<br />
</delphi><br />
<br />
On the application,<br />
<br />
<delphi><br />
var<br />
MyThread : TMyThread;<br />
begin<br />
MyThread := TMyThread.Create(True); // This way it doesn't start automatically<br />
...<br />
[Here the code initialises anything required before the threads starts executing]<br />
...<br />
MyThread.Resume;<br />
end;</delphi><br />
<br />
If you want to make your application more flexible you can create an event for the thread; this way your synchronized method won't be tightly coupled with a specific form or class: you can attach listeners to the thread's event. Here is an example:<br />
<br />
<delphi> Type<br />
TShowStatusEvent = procedure(Status: String) of Object;<br />
<br />
TMyThread = class(TThread)<br />
private<br />
fStatusText : string;<br />
FOnShowStatus: TShowStatusEvent;<br />
procedure ShowStatus;<br />
protected<br />
procedure Execute; override;<br />
public<br />
Constructor Create(CreateSuspended : boolean);<br />
property OnShowStatus: TShowStatusEvent read FOnShowStatus write FOnShowStatus;<br />
end;<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.ShowStatus;<br />
// this method is executed by the mainthread and can therefore access all GUI elements.<br />
begin<br />
if Assigned(FOnShowStatus) then<br />
begin<br />
FOnShowStatus(fStatusText);<br />
end;<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
newStatus : string;<br />
begin<br />
fStatusText := 'TMyThread Starting...';<br />
Synchronize(@Showstatus);<br />
fStatusText := 'TMyThread Running...';<br />
while (not Terminated) and ([any condition required]) do<br />
begin<br />
...<br />
[here goes the code of the main thread loop]<br />
...<br />
if NewStatus <> fStatusText then<br />
begin<br />
fStatusText := newStatus;<br />
Synchronize(@Showstatus);<br />
end;<br />
end;<br />
end;<br />
</delphi><br />
<br />
On the application,<br />
<br />
<delphi> Type<br />
TForm1 = class(TForm)<br />
Button1: TButton;<br />
Label1: TLabel;<br />
procedure FormCreate(Sender: TObject);<br />
procedure FormDestroy(Sender: TObject);<br />
private<br />
{ private declarations }<br />
MyThread: TMyThread; <br />
procedure ShowStatus(Status: string);<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
procedure TForm1.FormCreate(Sender: TObject);<br />
begin<br />
inherited;<br />
MyThread := TMyThread.Create(true);<br />
MyThread.OnShowStatus := @ShowStatus;<br />
end;<br />
<br />
procedure TForm1.FormDestroy(Sender: TObject);<br />
begin<br />
MyThread.Terminate;<br />
MyThread.Free;<br />
inherited;<br />
end;<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
begin<br />
MyThread.Resume;<br />
end;<br />
<br />
procedure TForm1.ShowStatus(Status: string);<br />
begin<br />
Label1.Caption := Status;<br />
end;</delphi><br />
<br />
== Special things to take care of ==<br />
<br />
=== Stack checking under Windows ===<br />
<br />
There is a potential headache in Windows with Threads if you use the -Ct (stack check) switch.<br />
For reasons not so clear the stack check will "trigger" on any TThread.Create if you use the default stack size.<br />
The only work-around for the moment is to simply not use -Ct switch. Note that it does NOT cause an exception in<br />
the main thread, but in the newly created one. This "looks" like if the thread was never started.<br />
<br />
A good code to check for this and other exceptions which can occur in thread creation is:<br />
<br />
<delphi>MyThread := TThread.Create(False);<br />
if Assigned(MyThread.FatalException) then<br />
raise MyThread.FatalException;</delphi><br />
<br />
This code will assure that any exception which occurred during thread creation will be raised in your main thread.<br />
<br />
== Units needed for a multi-threaded application ==<br />
You don´t need any special unit for this to work with Windows.<br />
However with Linux, Mac OS X and FreeBSD, you need the cthreads unit and it ''must'' be the first used unit of the project (the program unit, .lpr)!<br />
<br />
So, your Lazarus application code should look like:<br />
<br />
<Delphi><br />
program MyMultiThreadedProgram;<br />
{$mode objfpc}{$H+}<br />
uses<br />
{$ifdef unix}<br />
cthreads,<br />
cmem, // the c memory manager is on some systems much faster for multi-threading<br />
{$endif}<br />
Interfaces, // this includes the LCL widgetset<br />
Forms<br />
{ you can add units here },<br />
</Delphi><br />
<br />
If you forget this and you use TThread you will get this error on startup:<br />
This binary has no thread support compiled in.<br />
Recompile the application with a thread-driver in the program uses clause before other units using thread.<br />
<br />
=== Multithreading in packages ===<br />
Packages which uses multi-threading should add the '''-dUseCThreads''' flag to the custom usage options. Open the package editor of the package, then Options > Usage > Custom and add ''-dUseCThreads''. This will define this flag to all projects and packages using this package, including the IDE. The IDE and all new applications created by the IDE have already the following code in their .lpr file:<br />
<Delphi><br />
uses<br />
{$IFDEF UNIX}{$IFDEF UseCThreads}<br />
cthreads,<br />
cmem, // the c memory manager is on some systems much faster for multi-threading<br />
{$ENDIF}{$ENDIF}<br />
</DELPHI><br />
<br />
===Heaptrc===<br />
<br />
You can not use the -gh switch with the ''cmem'' unit. The -gh switch uses the heaptrc unit, which extends the heap manager. Therefore the '''heaptrc''' unit must be used '''after''' the '''cmem''' unit.<br />
<br />
<Delphi>uses<br />
{$IFDEF UNIX}{$IFDEF UseCThreads}<br />
cthreads,<br />
cmem, // the c memory manager is on some systems much faster for multi-threading<br />
{$ENDIF}{$ENDIF}<br />
heaptrc,</DELPHI><br />
<br />
== SMP Support ==<br />
The good news is that if your application works properly multi-threaded this way, it is already SMP enabled!<br />
<br />
== Debugging Multi-threaded Applications with Lazarus ==<br />
The debugging on Lazarus requires GDB and is rapidly becoming more and more fully featured and stable. However, there still exists a few Linux distributions with some problems. <br />
<br />
=== Debugging output ===<br />
In a single threaded application, you can simply write to console/terminal/whatever and the order of the lines is the same as they were written.<br />
In multi-threaded application things are more complicated. If two threads are writing, say a line is written by thread A before a line by thread B, then the lines are not necessarily written in that order. It can even happen, that a thread writes its output, while the other thread is writing a line.<br />
<br />
The LCLProc unit contains several functions, to let each thread write to its own log file:<br />
<delphi> procedure DbgOutThreadLog(const Msg: string); overload;<br />
procedure DebuglnThreadLog(const Msg: string); overload;<br />
procedure DebuglnThreadLog(Args: array of const); overload;<br />
procedure DebuglnThreadLog; overload;</delphi><br />
<br />
For example:<br />
Instead of ''writeln('Some text ',123);'' use <br />
DebuglnThreadLog(['Some text ',123]);<br />
<br />
This will append a line 'Some text 123' to '''Log<PID>.txt''', where <PID> is the process ID of the current thread.<br />
<br />
It is a good idea to remove the log files before each run:<br />
rm -f Log* && ./project1<br />
<br />
=== Linux ===<br />
If you try to debug a multi-threaded application on Linux, you will have one big problem: the Desktop Manager on X server will hang. When that happens you can simply exit out of that session and create a new session by pressing CTRL+ALT+F3. That will give you a new session prompt. Once there enter sudo /etc/init.d/gdm restart. This will restart the desktop manager and get you back into your desktop.<br />
<br />
One method that solved this problem for Ubuntu x64 is to set the Project options for debugging required extra information file...<br />
<br />
Project Options -> Compiler Options -> Linking -> Debugging: Check Use external gdb debug symbols file (-Xg).<br />
<br />
If the above solution does not work a workaround is:<br />
<br />
Create a new instance of X with:<br />
<br />
X :1 &<br />
<br />
It will open, and when you switch to another desktop (the one you are working with pressing CTRL+ALT+F7), you will be able to go back to the new graphical desktop with CTRL+ALT+F8 (if this combination does not work, try with CTRL+ALT+F2... this one worked on [http://www.slackware.com Slackware]).<br />
<br />
Then you could, if you want, create a desktop session on the X started with:<br />
<br />
gnome-session --display=:1 &<br />
<br />
Then, in Lazarus, on the run parameters dialog for the project, check "Use display" and enter :1.<br />
<br />
Now the application will run on the second X server and you will be able to debug it on the first one.<br />
<br />
This was tested with Free Pascal 2.0 and Lazarus 0.9.10 on Windows and Linux.<br />
<br />
<br />
----<br />
<br />
Instead of creating a new X session, one can use [http://en.wikipedia.org/wiki/Xnest Xnest]. Xnest is a X session on a window. Using it X server didn't lock while debugging threads, and it's much easier to debug without keeping changing terminals.<br />
<br />
The command line to run Xnest is<br />
<br />
Xnest :1 -ac<br />
<br />
to create a X session on :1, and disabling access control.<br />
<br />
== Widgetsets ==<br />
The win32, the gtk and the carbon interfaces fully support multi-threading. This means, TThread, critical sections and Synchronize work.<br />
<br />
== Critical sections ==<br />
A ''critical section'' is an object used to make sure, that some part of the code is executed only by one thread at a time. A critical section needs to be created/initialized before it can be used and be freed when it is not needed anymore.<br />
<br />
Critical sections are normally used this way:<br />
<br />
Add the unit SyncObjs.<br />
<br />
Declare the section (globally for all threads which should access the section):<br />
MyCriticalSection: TRTLCriticalSection;<br />
<br />
Create the section:<br />
InitializeCriticalSection(MyCriticalSection);<br />
<br />
Run some threads. Doing something exclusively<br />
<delphi><br />
EnterCriticalSection(MyCriticalSection);<br />
try<br />
// access some variables, write files, send some network packets, etc<br />
finally<br />
LeaveCriticalSection(MyCriticalSection);<br />
end;</delphi><br />
<br />
After all threads terminated, free it:<br />
DeleteCriticalSection(MyCriticalSection);<br />
<br />
As an alternative, you can use a TCriticalSection object. The creation does the initialization, the Enter method does the EnterCriticalSection, the Leave method does the LeaveCriticalSection and the destruction of the object does the deletion.<br />
<br />
For example: 5 threads incrementing a counter.<br />
See lazarus/examples/multithreading/criticalsectionexample1.lpi<br />
<br />
'''BEWARE:''' There are two sets of the above 4 functions. The RTL and the LCL ones. The LCL ones are defined in the unit LCLIntf and LCLType. Both work pretty much the same. You can use both at the same time in your application, but you should not use a RTL function with an LCL Critical Section and vice versus.<br />
<br />
<br />
=== Sharing Variables ===<br />
If some threads share a variable, that is read only, then there is nothing to worry about. Just read it.<br />
But if one or several threads changes the variable, then you must make sure, that only one thread accesses the variables at a time.<br />
<br />
For example: 5 threads incrementing a counter.<br />
See lazarus/examples/multithreading/criticalsectionexample1.lpi<br />
<br />
== Waiting for another thread ==<br />
If a thread A needs a result of another thread B, it must wait, till B has finished. <br />
<br />
'''Important:''' The main thread should never wait for another thread. Instead use Synchronize (see above).<br />
<br />
See for an example: lazarus/examples/multithreading/waitforexample1.lpi<br />
<br />
<delphi>{ TThreadA }<br />
<br />
procedure TThreadA.Execute;<br />
begin<br />
Form1.ThreadB:=TThreadB.Create(false);<br />
// create event<br />
WaitForB:=RTLEventCreate;<br />
while not Application.Terminated do begin<br />
// wait infinitely (until B wakes A)<br />
RtlEventWaitFor(WaitForB);<br />
writeln('A: ThreadB.Counter='+IntToStr(Form1.ThreadB.Counter));<br />
end;<br />
end;<br />
<br />
{ TThreadB }<br />
<br />
procedure TThreadB.Execute;<br />
var<br />
i: Integer;<br />
begin<br />
Counter:=0;<br />
while not Application.Terminated do begin<br />
// B: Working ...<br />
Sleep(1500);<br />
inc(Counter);<br />
// wake A<br />
RtlEventSetEvent(Form1.ThreadA.WaitForB);<br />
end;<br />
end;</delphi><br />
<br />
Note: RtlEventSetEvent can be called before RtlEventWaitFor. Then RtlEventWaitFor will return immediately. Use RTLeventResetEvent to clear a flag.<br />
<br />
== Fork ==<br />
When forking in a multi-threaded application, be aware that any threads created and running BEFORE the fork (or fpFork) call, will NOT be running in the child process. As stated on the fork() man page, any threads that were running before the fork call, their state will be undefined.<br />
<br />
So be aware of any threads initializing before the call (including on the initialization section). They will NOT work.<br />
<br />
== Parallel procedures/loops ==<br />
A special case of multi threading is running a single procedure in parallel. See [[Parallel procedures]].<br />
<br />
== Distributed computing ==<br />
The next higher steps after multi threading is running the threads on multiple machines. <br />
* You can use one of the TCP suites like synapse, lnet or indy for communications. This gives you maximum flexibility and is mostly used for loosely connected Client / Server applications.<br />
* You can use message passing libraries like [[MPICH]], which are used for HPC (High Performance Computing) on clusters.<br />
<br />
<br />
<br />
== External threads ==<br />
To make Free Pascal's threading system to work properly, each newly created FPC thread needs to be initialized (more exactly, the exception, I/O system and threadvar system per thread needs to be initialized so threadvars and heap are working). That is fully automatically done for you if you use BeginThread (or indirectly by using the TThread class). However, if you use threads that were created without BeginThread (i.e. external threads), additional work (currently) might be required. External threads also include those that were created in external C libraries (.DLL/.so). <br />
<br />
<br />
Things to consider when using external threads (might not be needed in all or future compiler versions): <br />
<br />
* Do not use external threads at all - use FPC threads. If can you can get control over how the thread is created, create the thread by yourself by using BeginThread.<br />
<br />
If the calling convention doesn't fit (e.g. if your original thread function needs cdecl calling convention but BeginThread needs pascal convention, create a record, store the original required thread function in it, and call that function in your pascal thread function: <br />
<br />
<Delphi>type <br />
TCdeclThreadFunc = function (user_data:Pointer):Pointer;cdecl; <br />
<br />
PCdeclThreadFuncData = ^TCdeclThreadFunc; <br />
TCdeclThreadFuncData = record<br />
Func: TCdeclThreadFunc; //cdecl function<br />
Data: Pointer; //original data<br />
end; <br />
<br />
// The Pascal thread calls the cdecl function<br />
function C2P_Translator(FuncData: pointer) : ptrint;<br />
var<br />
ThreadData: TCdeclThreadFuncData;<br />
begin <br />
ThreadData := PCdeclThreadFuncData(FuncData)^;<br />
Result := ptrint(ThreadData.Func(ThreadData.Data));<br />
end; <br />
<br />
procedure CreatePascalThread;<br />
var<br />
ThreadData: PCdeclThreadFunc; <br />
begin <br />
New(ThreadData);<br />
// this is the desired cdecl thread function<br />
ThreadData^.Func := func; <br />
ThreadData^.Data := user_data; <br />
// this creates the Pascal thread<br />
BeginThread(@C2P_Translator, ThreadData );<br />
end;</Delphi><br />
<br />
<br />
* Initialize the FPC's threading system by creating a dummy thread. If you don't create any Pascal thread in your app, the thread system won't be initialized (and thus threadvars won't work and thus heap will not work correctly). <br />
<br />
<Delphi>type<br />
tc = class(tthread)<br />
procedure execute;override;<br />
end;<br />
<br />
procedure tc.execute;<br />
begin<br />
end;<br />
<br />
{ main program } <br />
begin<br />
{ initialise threading system }<br />
with tc.create(false) do<br />
begin<br />
waitfor;<br />
free;<br />
end;<br />
{ ... your code follows } <br />
end.</Delphi><br />
<br />
(After the threading system is initialized, the runtime may set the system variable "IsMultiThread" to true which is used by FPC routines to perform locks here and there. You should not set this variable manually.) <br />
<br />
<br />
* If for some reason this doesn't work for you, try this code in your external thread function:<br />
<br />
<Delphi>function ExternalThread(param: Pointer): LongInt; stdcall;<br />
var<br />
tm: TThreadManager;<br />
begin<br />
GetThreadManager(tm);<br />
tm.AllocateThreadVars;<br />
InitThread(1000000); // adjust inital stack size here<br />
<br />
{ do something threaded here ... }<br />
<br />
Result:=0;<br />
end;</Delphi><br />
<br />
<br />
=== Identifying external threads ===<br />
Sometimes you even don't know if you have to deal with external threads (e.g. if some C library makes a callback). This can help to analyse this: <br />
<br />
1. Ask the OS for the ID of the current thread at your application's start <br />
<br />
<Delphi>Win32: GetCurrentThreadID();<br />
Darwin: GetThreadID(); <br />
Linux: TThreadID(pthread_self);</Delphi><br />
<br />
2. Ask again for the ID of the current thread inside the thread function and compare this by the result of step 1.<br />
<br />
==See also==<br />
<br />
* [[Streaming components]]<br />
* [[Manager Worker Threads System]]<br />
* [[Example of multi-threaded application: array of threads]]<br />
[[Category:Tutorials]]</div>Mvampirehttps://wiki.freepascal.org/index.php?title=Example_of_multi-threaded_application:_array_of_threads&diff=44715Example of multi-threaded application: array of threads2010-09-07T21:18:44Z<p>Mvampire: Array of threads moved to Example of multi-threaded application: array of threads</p>
<hr />
<div>Here I want to show an example how to create many threads and wait while they will not finish their jobs. I'm writing this tutorial because it was not obvious for me to write such a program after reading Multithread Application Tutorial. I was writing my application for Linux x86_64.<br />
<br />
Let's assume that we have the following loop:<br />
<delphi><br />
var results: integer;<br />
File: text;<br />
begin<br />
<br />
...<br />
<br />
for i:=1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
...<br />
</delphi><br />
This loop runs procedure that "calculate" some 'result'. After this we write this array to a text file... Now we want to divide this job to many parallel threads.<br />
<br />
To do this, You need to perform the following steps:<br />
<br />
1. Manage memory. Your threads can read any variables any time You need, but they shouldn't write any data to same variables (global) at the same time. So Your threads will not be able to write anything to one variable like 'result'. And they will not be able to write it to file. Moreover, I was interested to write my results to file in order (from 1 to n), and threads will not do this. So first step is to create global array of results (1 cell for 1 thread). If your procedure write something to many variables - you can create several arrays or array of record.<br />
<br />
I use a separate unit for global variables:<br />
<delphi><br />
unit Global;<br />
{$mode objfpc}{$H+}<br />
interface<br />
uses Classes, SysUtils; <br />
<br />
var result: array [1..100] of threads; //We will create not more than 100 threads.<br />
<br />
implementation<br />
end.<br />
</delphi><br />
2. Add threads class. I also use a separate unit for it. So I need to describe 'property Terminated' as the public (to use it from the main program).<br />
<delphi><br />
unit MyThreads;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils; <br />
<br />
Type<br />
TMyThread = class(TThread)<br />
private<br />
protected<br />
procedure Execute; override;<br />
public<br />
start,finish: integer; //I will initialise these fields before running the thread. So each thread will receive a part of task.<br />
property Terminated;<br />
Constructor Create(CreateSuspended : boolean);<br />
end;<br />
<br />
implementation<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
i: integer;<br />
begin<br />
i:=start;<br />
while (not Terminated) and (i<=finish) do<br />
begin<br />
MyProcedure(result[i]);<br />
inc(i);<br />
if i=finish+1 then Terminate;<br />
end;<br />
end;<br />
end. <br />
</delphi><br />
3. Rewrite main program.<br />
<br />
You need to add 'cthreads' to the main unit, not to unit with threads!<br />
<br />
Tips: in my Lazarus IDE I was not able to debug multi-threading applications if I don't use 'pthreads'. I have red that if you use 'cmem', the program works faster, but I strongly recommend you to check it for any particular case (my program hangs when I use 'cmem').<br />
<delphi><br />
uses cthreads,<br />
// cmem,<br />
// pthreads,<br />
Classes, SysUtils, CustApp, MyThreads, Global<br />
<br />
var<br />
ThreadArray: array [1..100] of TMyThread; //We will create no more than 100 threads.<br />
i, number_of_threads: integer;<br />
begin<br />
<br />
number of threads:=100;<br />
<br />
while n div number_of_threads <1 do dec(number_of_threads); //If n<100 then we don't have enough tasks for 100 threads.<br />
<br />
for i:=1 to number_of_threads do begin<br />
ThreadArray[i]:= TMyThread.Create(True);<br />
ThreadArray[i].start:=(i-1)*(n div number_of_threads)+1;;<br />
ThreadArray[i].finish:=i*(n div number_of_threads);<br />
ThreadArray[i].Resume;<br />
end;<br />
<br />
for i:=1 to 10 do if not ThreadArray[i].Terminated then Sleep(10); //Waiting while threads will not finish their job.<br />
<br />
for i:=1 to n do Writeln(file,result[i]); //Writing results in order.<br />
<br />
//Now we should finish part of the tast that couldn't be divided to 100 threads.<br />
//For examle, if n=10050 then 10000 will be divided to 100 threads. And last 50 (< 100) we should finish in non-parallel mode<br />
//That's how I do this. Instead, You can write something like: 'if i=number_of_threads then ThreadArray[i].finish:=n;'<br />
//In this case last thread will finish the job.<br />
<br />
if n mod number_of_threads<>0 then<br />
for i:=(n div number_of_threads) + 1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
Terminate;<br />
end;<br />
</delphi></div>Mvampirehttps://wiki.freepascal.org/index.php?title=Multithreaded_Application_Tutorial&diff=44714Multithreaded Application Tutorial2010-09-07T21:17:58Z<p>Mvampire: /* See also */</p>
<hr />
<div>{{Multithreaded Application Tutorial}}<br />
<br />
== Overview ==<br />
This page will try to explain how to write and debug a multi-threaded application with Free Pascal and Lazarus. A multi-threaded application is one that creates two or more threads of execution that work at the same time. If you are new to multi-threading, please read the paragraph "Do you need multi-threading?" to determine whether it is really required; this may save you many headaches.<br />
<br />
One of the threads is called the Main Thread. The Main Thread is the one that is created by the Operating System once our application starts. The Main Thread '''must be''' the only thread that updates the components that interfaces with the user: otherwise, the application may hang.<br />
<br />
The main idea is that the application can do some processing in background in a second thread while the user can continue working using the main thread.<br />
<br />
Another use of threads is just to have a better responding application. If you create an application, and when the user presses a button the application starts processing a big job... and while processing, the screen stops responding, and gives the user the impression that the application is frozen, a poor or misleading impression will be created. If the big job runs in a second thread, the application keeps responding (almost) as if it were idle. In this case it is a good idea, before starting the thread, to disable the buttons of the form to avoid the user starting more than one thread for the job.<br />
<br />
Another use of multi-threading may be a server application that is able to respond to many clients at the same time.<br />
<br />
== Do you need multi-threading? ==<br />
<br />
If you are new to multi-threading and you only want to make your application more responsive while your application performs moderately long-running tasks, then multi-threading may be more than is required.<br />
Multi-threaded applications are always more difficult to debug and they are often much more complex; in many cases you don't need multi-threading. A single thread is enough. If you can split up the time-consuming task into several smaller chunks, then instead you should use '''Application.ProcessMessages'''. This method allows the LCL to handle all waiting messages and returns.<br />
The central idea is to call Application.ProcessMessages at regular intervals during the execution of a long-running task to determine whether the user has clicked on something, or a progress indicator must be repainted, and so on.<br />
<br />
For example: Reading a big file and process it. <br />
See examples/multithreading/singlethreadingexample1.lpi.<br />
<br />
Multi-threading is only needed for<br />
* blocking handles, like network communications<br />
* using multiple processors simultaneously (SMP)<br />
* algorithms and library calls that must be called through an API an as such cannot be split up into smaller parts.<br />
<br />
== The TThread Class ==<br />
<br />
The following example can be found in the examples/multithreading/ directory.<br />
<br />
To create a multi-threaded application, the easiest way is to use the TThread Class. This class permits the creation of an additional thread (alongside the main thread) in a simple way. Normally you are required to override only 2 methods: the Create constructor, and the Execute method.<br />
<br />
In the constructor, you will prepare the thread to run. You will set the initial values of the variables or properties you need. The original constructor of TThread requires a parameter called Suspended. As you might expect, setting Suspended = True will prevent the thread starting automatically after the creation. If Suspended = False, the thread will start running just after the creation. If the thread is created suspended, then it will run only after the Resume method is called.<br />
<br />
As of FPC version 2.0.1 and later, TThread.Create also has an implicit parameter for Stack Size. You can now change the default stack size of each thread you create if you need it. Deep procedure call recursions in a thread are a good example. If you don't specify the stack size parameter, a default OS stack size is used.<br />
<br />
In the overridden Execute method you will write the code that will run on the thread.<br />
<br />
The TThread class has one important property: <br />
Terminated : boolean;<br />
<br />
If the thread has a loop (and this is typical), the loop should be exited when Terminated is true (it is false by default). Within each pass, the value of Terminated must be checked, and if it is true then the loop should be exited as quickly as is appropriate, after any necessary cleanup. Bear in mind that the Terminate method does not do anything by default: the .Execute method must explicitly implement support for it to quit its job.<br />
<br />
As we explained earlier, the thread should not interact with the visible components. Updates to visible components must be made within the context of the main thread. To do this, a TThread method called Synchronize exists. Synchronize requires a method (that takes no parameters) as an argument. When you call that method through Synchronize(@MyMethod), the thread execution will be paused, the code of MyMethod will run in the main thread, and then the thread execution will be resumed. The exact working of Synchronize depends on the platform, but basically it does this: it posts a message onto the main message queue and goes to sleep. Eventually the main thread processes the message and calls MyMethod. This way MyMethod is called without context, that means not during a mouse down event or during paint event, but after. After the main thread executed MyMethod, it wakes the sleeping Thread and processes the next message. The Thread then continues.<br />
<br />
There is another important property of TThread: FreeOnTerminate. If this property is true, the thread object is automatically freed when the thread execution (.Execute method) stops. Otherwise the application will need to free it manually.<br />
<br />
Example:<br />
<br />
<delphi> Type<br />
TMyThread = class(TThread)<br />
private<br />
fStatusText : string;<br />
procedure ShowStatus;<br />
protected<br />
procedure Execute; override;<br />
public<br />
Constructor Create(CreateSuspended : boolean);<br />
end;<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.ShowStatus;<br />
// this method is executed by the mainthread and can therefore access all GUI elements.<br />
begin<br />
Form1.Caption := fStatusText;<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
newStatus : string;<br />
begin<br />
fStatusText := 'TMyThread Starting...';<br />
Synchronize(@Showstatus);<br />
fStatusText := 'TMyThread Running...';<br />
while (not Terminated) and ([any condition required]) do<br />
begin<br />
...<br />
[here goes the code of the main thread loop]<br />
...<br />
if NewStatus <> fStatusText then<br />
begin<br />
fStatusText := newStatus;<br />
Synchronize(@Showstatus);<br />
end;<br />
end;<br />
end;<br />
</delphi><br />
<br />
On the application,<br />
<br />
<delphi><br />
var<br />
MyThread : TMyThread;<br />
begin<br />
MyThread := TMyThread.Create(True); // This way it doesn't start automatically<br />
...<br />
[Here the code initialises anything required before the threads starts executing]<br />
...<br />
MyThread.Resume;<br />
end;</delphi><br />
<br />
If you want to make your application more flexible you can create an event for the thread; this way your synchronized method won't be tightly coupled with a specific form or class: you can attach listeners to the thread's event. Here is an example:<br />
<br />
<delphi> Type<br />
TShowStatusEvent = procedure(Status: String) of Object;<br />
<br />
TMyThread = class(TThread)<br />
private<br />
fStatusText : string;<br />
FOnShowStatus: TShowStatusEvent;<br />
procedure ShowStatus;<br />
protected<br />
procedure Execute; override;<br />
public<br />
Constructor Create(CreateSuspended : boolean);<br />
property OnShowStatus: TShowStatusEvent read FOnShowStatus write FOnShowStatus;<br />
end;<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.ShowStatus;<br />
// this method is executed by the mainthread and can therefore access all GUI elements.<br />
begin<br />
if Assigned(FOnShowStatus) then<br />
begin<br />
FOnShowStatus(fStatusText);<br />
end;<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
newStatus : string;<br />
begin<br />
fStatusText := 'TMyThread Starting...';<br />
Synchronize(@Showstatus);<br />
fStatusText := 'TMyThread Running...';<br />
while (not Terminated) and ([any condition required]) do<br />
begin<br />
...<br />
[here goes the code of the main thread loop]<br />
...<br />
if NewStatus <> fStatusText then<br />
begin<br />
fStatusText := newStatus;<br />
Synchronize(@Showstatus);<br />
end;<br />
end;<br />
end;<br />
</delphi><br />
<br />
On the application,<br />
<br />
<delphi> Type<br />
TForm1 = class(TForm)<br />
Button1: TButton;<br />
Label1: TLabel;<br />
procedure FormCreate(Sender: TObject);<br />
procedure FormDestroy(Sender: TObject);<br />
private<br />
{ private declarations }<br />
MyThread: TMyThread; <br />
procedure ShowStatus(Status: string);<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
procedure TForm1.FormCreate(Sender: TObject);<br />
begin<br />
inherited;<br />
MyThread := TMyThread.Create(true);<br />
MyThread.OnShowStatus := @ShowStatus;<br />
end;<br />
<br />
procedure TForm1.FormDestroy(Sender: TObject);<br />
begin<br />
MyThread.Terminate;<br />
MyThread.Free;<br />
inherited;<br />
end;<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
begin<br />
MyThread.Resume;<br />
end;<br />
<br />
procedure TForm1.ShowStatus(Status: string);<br />
begin<br />
Label1.Caption := Status;<br />
end;</delphi><br />
<br />
== Special things to take care of ==<br />
<br />
=== Stack checking under Windows ===<br />
<br />
There is a potential headache in Windows with Threads if you use the -Ct (stack check) switch.<br />
For reasons not so clear the stack check will "trigger" on any TThread.Create if you use the default stack size.<br />
The only work-around for the moment is to simply not use -Ct switch. Note that it does NOT cause an exception in<br />
the main thread, but in the newly created one. This "looks" like if the thread was never started.<br />
<br />
A good code to check for this and other exceptions which can occur in thread creation is:<br />
<br />
<delphi>MyThread := TThread.Create(False);<br />
if Assigned(MyThread.FatalException) then<br />
raise MyThread.FatalException;</delphi><br />
<br />
This code will assure that any exception which occurred during thread creation will be raised in your main thread.<br />
<br />
== Units needed for a multi-threaded application ==<br />
You don´t need any special unit for this to work with Windows.<br />
However with Linux, Mac OS X and FreeBSD, you need the cthreads unit and it ''must'' be the first used unit of the project (the program unit, .lpr)!<br />
<br />
So, your Lazarus application code should look like:<br />
<br />
<Delphi><br />
program MyMultiThreadedProgram;<br />
{$mode objfpc}{$H+}<br />
uses<br />
{$ifdef unix}<br />
cthreads,<br />
cmem, // the c memory manager is on some systems much faster for multi-threading<br />
{$endif}<br />
Interfaces, // this includes the LCL widgetset<br />
Forms<br />
{ you can add units here },<br />
</Delphi><br />
<br />
If you forget this and you use TThread you will get this error on startup:<br />
This binary has no thread support compiled in.<br />
Recompile the application with a thread-driver in the program uses clause before other units using thread.<br />
<br />
=== Multithreading in packages ===<br />
Packages which uses multi-threading should add the '''-dUseCThreads''' flag to the custom usage options. Open the package editor of the package, then Options > Usage > Custom and add ''-dUseCThreads''. This will define this flag to all projects and packages using this package, including the IDE. The IDE and all new applications created by the IDE have already the following code in their .lpr file:<br />
<Delphi><br />
uses<br />
{$IFDEF UNIX}{$IFDEF UseCThreads}<br />
cthreads,<br />
cmem, // the c memory manager is on some systems much faster for multi-threading<br />
{$ENDIF}{$ENDIF}<br />
</DELPHI><br />
<br />
===Heaptrc===<br />
<br />
You can not use the -gh switch with the ''cmem'' unit. The -gh switch uses the heaptrc unit, which extends the heap manager. Therefore the '''heaptrc''' unit must be used '''after''' the '''cmem''' unit.<br />
<br />
<Delphi>uses<br />
{$IFDEF UNIX}{$IFDEF UseCThreads}<br />
cthreads,<br />
cmem, // the c memory manager is on some systems much faster for multi-threading<br />
{$ENDIF}{$ENDIF}<br />
heaptrc,</DELPHI><br />
<br />
== SMP Support ==<br />
The good news is that if your application works properly multi-threaded this way, it is already SMP enabled!<br />
<br />
== Debugging Multi-threaded Applications with Lazarus ==<br />
The debugging on Lazarus requires GDB and is rapidly becoming more and more fully featured and stable. However, there still exists a few Linux distributions with some problems. <br />
<br />
=== Debugging output ===<br />
In a single threaded application, you can simply write to console/terminal/whatever and the order of the lines is the same as they were written.<br />
In multi-threaded application things are more complicated. If two threads are writing, say a line is written by thread A before a line by thread B, then the lines are not necessarily written in that order. It can even happen, that a thread writes its output, while the other thread is writing a line.<br />
<br />
The LCLProc unit contains several functions, to let each thread write to its own log file:<br />
<delphi> procedure DbgOutThreadLog(const Msg: string); overload;<br />
procedure DebuglnThreadLog(const Msg: string); overload;<br />
procedure DebuglnThreadLog(Args: array of const); overload;<br />
procedure DebuglnThreadLog; overload;</delphi><br />
<br />
For example:<br />
Instead of ''writeln('Some text ',123);'' use <br />
DebuglnThreadLog(['Some text ',123]);<br />
<br />
This will append a line 'Some text 123' to '''Log<PID>.txt''', where <PID> is the process ID of the current thread.<br />
<br />
It is a good idea to remove the log files before each run:<br />
rm -f Log* && ./project1<br />
<br />
=== Linux ===<br />
If you try to debug a multi-threaded application on Linux, you will have one big problem: the Desktop Manager on X server will hang. When that happens you can simply exit out of that session and create a new session by pressing CTRL+ALT+F3. That will give you a new session prompt. Once there enter sudo /etc/init.d/gdm restart. This will restart the desktop manager and get you back into your desktop.<br />
<br />
One method that solved this problem for Ubuntu x64 is to set the Project options for debugging required extra information file...<br />
<br />
Project Options -> Compiler Options -> Linking -> Debugging: Check Use external gdb debug symbols file (-Xg).<br />
<br />
If the above solution does not work a workaround is:<br />
<br />
Create a new instance of X with:<br />
<br />
X :1 &<br />
<br />
It will open, and when you switch to another desktop (the one you are working with pressing CTRL+ALT+F7), you will be able to go back to the new graphical desktop with CTRL+ALT+F8 (if this combination does not work, try with CTRL+ALT+F2... this one worked on [http://www.slackware.com Slackware]).<br />
<br />
Then you could, if you want, create a desktop session on the X started with:<br />
<br />
gnome-session --display=:1 &<br />
<br />
Then, in Lazarus, on the run parameters dialog for the project, check "Use display" and enter :1.<br />
<br />
Now the application will run on the second X server and you will be able to debug it on the first one.<br />
<br />
This was tested with Free Pascal 2.0 and Lazarus 0.9.10 on Windows and Linux.<br />
<br />
<br />
----<br />
<br />
Instead of creating a new X session, one can use [http://en.wikipedia.org/wiki/Xnest Xnest]. Xnest is a X session on a window. Using it X server didn't lock while debugging threads, and it's much easier to debug without keeping changing terminals.<br />
<br />
The command line to run Xnest is<br />
<br />
Xnest :1 -ac<br />
<br />
to create a X session on :1, and disabling access control.<br />
<br />
== Widgetsets ==<br />
The win32, the gtk and the carbon interfaces fully support multi-threading. This means, TThread, critical sections and Synchronize work.<br />
<br />
== Critical sections ==<br />
A ''critical section'' is an object used to make sure, that some part of the code is executed only by one thread at a time. A critical section needs to be created/initialized before it can be used and be freed when it is not needed anymore.<br />
<br />
Critical sections are normally used this way:<br />
<br />
Add the unit SyncObjs.<br />
<br />
Declare the section (globally for all threads which should access the section):<br />
MyCriticalSection: TRTLCriticalSection;<br />
<br />
Create the section:<br />
InitializeCriticalSection(MyCriticalSection);<br />
<br />
Run some threads. Doing something exclusively<br />
<delphi><br />
EnterCriticalSection(MyCriticalSection);<br />
try<br />
// access some variables, write files, send some network packets, etc<br />
finally<br />
LeaveCriticalSection(MyCriticalSection);<br />
end;</delphi><br />
<br />
After all threads terminated, free it:<br />
DeleteCriticalSection(MyCriticalSection);<br />
<br />
As an alternative, you can use a TCriticalSection object. The creation does the initialization, the Enter method does the EnterCriticalSection, the Leave method does the LeaveCriticalSection and the destruction of the object does the deletion.<br />
<br />
For example: 5 threads incrementing a counter.<br />
See lazarus/examples/multithreading/criticalsectionexample1.lpi<br />
<br />
'''BEWARE:''' There are two sets of the above 4 functions. The RTL and the LCL ones. The LCL ones are defined in the unit LCLIntf and LCLType. Both work pretty much the same. You can use both at the same time in your application, but you should not use a RTL function with an LCL Critical Section and vice versus.<br />
<br />
<br />
=== Sharing Variables ===<br />
If some threads share a variable, that is read only, then there is nothing to worry about. Just read it.<br />
But if one or several threads changes the variable, then you must make sure, that only one thread accesses the variables at a time.<br />
<br />
For example: 5 threads incrementing a counter.<br />
See lazarus/examples/multithreading/criticalsectionexample1.lpi<br />
<br />
== Waiting for another thread ==<br />
If a thread A needs a result of another thread B, it must wait, till B has finished. <br />
<br />
'''Important:''' The main thread should never wait for another thread. Instead use Synchronize (see above).<br />
<br />
See for an example: lazarus/examples/multithreading/waitforexample1.lpi<br />
<br />
<delphi>{ TThreadA }<br />
<br />
procedure TThreadA.Execute;<br />
begin<br />
Form1.ThreadB:=TThreadB.Create(false);<br />
// create event<br />
WaitForB:=RTLEventCreate;<br />
while not Application.Terminated do begin<br />
// wait infinitely (until B wakes A)<br />
RtlEventWaitFor(WaitForB);<br />
writeln('A: ThreadB.Counter='+IntToStr(Form1.ThreadB.Counter));<br />
end;<br />
end;<br />
<br />
{ TThreadB }<br />
<br />
procedure TThreadB.Execute;<br />
var<br />
i: Integer;<br />
begin<br />
Counter:=0;<br />
while not Application.Terminated do begin<br />
// B: Working ...<br />
Sleep(1500);<br />
inc(Counter);<br />
// wake A<br />
RtlEventSetEvent(Form1.ThreadA.WaitForB);<br />
end;<br />
end;</delphi><br />
<br />
Note: RtlEventSetEvent can be called before RtlEventWaitFor. Then RtlEventWaitFor will return immediately. Use RTLeventResetEvent to clear a flag.<br />
<br />
== Fork ==<br />
When forking in a multi-threaded application, be aware that any threads created and running BEFORE the fork (or fpFork) call, will NOT be running in the child process. As stated on the fork() man page, any threads that were running before the fork call, their state will be undefined.<br />
<br />
So be aware of any threads initializing before the call (including on the initialization section). They will NOT work.<br />
<br />
== Parallel procedures/loops ==<br />
A special case of multi threading is running a single procedure in parallel. See [[Parallel procedures]].<br />
<br />
== Distributed computing ==<br />
The next higher steps after multi threading is running the threads on multiple machines. <br />
* You can use one of the TCP suites like synapse, lnet or indy for communications. This gives you maximum flexibility and is mostly used for loosely connected Client / Server applications.<br />
* You can use message passing libraries like [[MPICH]], which are used for HPC (High Performance Computing) on clusters.<br />
<br />
<br />
<br />
== External threads ==<br />
To make Free Pascal's threading system to work properly, each newly created FPC thread needs to be initialized (more exactly, the exception, I/O system and threadvar system per thread needs to be initialized so threadvars and heap are working). That is fully automatically done for you if you use BeginThread (or indirectly by using the TThread class). However, if you use threads that were created without BeginThread (i.e. external threads), additional work (currently) might be required. External threads also include those that were created in external C libraries (.DLL/.so). <br />
<br />
<br />
Things to consider when using external threads (might not be needed in all or future compiler versions): <br />
<br />
* Do not use external threads at all - use FPC threads. If can you can get control over how the thread is created, create the thread by yourself by using BeginThread.<br />
<br />
If the calling convention doesn't fit (e.g. if your original thread function needs cdecl calling convention but BeginThread needs pascal convention, create a record, store the original required thread function in it, and call that function in your pascal thread function: <br />
<br />
<Delphi>type <br />
TCdeclThreadFunc = function (user_data:Pointer):Pointer;cdecl; <br />
<br />
PCdeclThreadFuncData = ^TCdeclThreadFunc; <br />
TCdeclThreadFuncData = record<br />
Func: TCdeclThreadFunc; //cdecl function<br />
Data: Pointer; //original data<br />
end; <br />
<br />
// The Pascal thread calls the cdecl function<br />
function C2P_Translator(FuncData: pointer) : ptrint;<br />
var<br />
ThreadData: TCdeclThreadFuncData;<br />
begin <br />
ThreadData := PCdeclThreadFuncData(FuncData)^;<br />
Result := ptrint(ThreadData.Func(ThreadData.Data));<br />
end; <br />
<br />
procedure CreatePascalThread;<br />
var<br />
ThreadData: PCdeclThreadFunc; <br />
begin <br />
New(ThreadData);<br />
// this is the desired cdecl thread function<br />
ThreadData^.Func := func; <br />
ThreadData^.Data := user_data; <br />
// this creates the Pascal thread<br />
BeginThread(@C2P_Translator, ThreadData );<br />
end;</Delphi><br />
<br />
<br />
* Initialize the FPC's threading system by creating a dummy thread. If you don't create any Pascal thread in your app, the thread system won't be initialized (and thus threadvars won't work and thus heap will not work correctly). <br />
<br />
<Delphi>type<br />
tc = class(tthread)<br />
procedure execute;override;<br />
end;<br />
<br />
procedure tc.execute;<br />
begin<br />
end;<br />
<br />
{ main program } <br />
begin<br />
{ initialise threading system }<br />
with tc.create(false) do<br />
begin<br />
waitfor;<br />
free;<br />
end;<br />
{ ... your code follows } <br />
end.</Delphi><br />
<br />
(After the threading system is initialized, the runtime may set the system variable "IsMultiThread" to true which is used by FPC routines to perform locks here and there. You should not set this variable manually.) <br />
<br />
<br />
* If for some reason this doesn't work for you, try this code in your external thread function:<br />
<br />
<Delphi>function ExternalThread(param: Pointer): LongInt; stdcall;<br />
var<br />
tm: TThreadManager;<br />
begin<br />
GetThreadManager(tm);<br />
tm.AllocateThreadVars;<br />
InitThread(1000000); // adjust inital stack size here<br />
<br />
{ do something threaded here ... }<br />
<br />
Result:=0;<br />
end;</Delphi><br />
<br />
<br />
=== Identifying external threads ===<br />
Sometimes you even don't know if you have to deal with external threads (e.g. if some C library makes a callback). This can help to analyse this: <br />
<br />
1. Ask the OS for the ID of the current thread at your application's start <br />
<br />
<Delphi>Win32: GetCurrentThreadID();<br />
Darwin: GetThreadID(); <br />
Linux: TThreadID(pthread_self);</Delphi><br />
<br />
2. Ask again for the ID of the current thread inside the thread function and compare this by the result of step 1.<br />
<br />
==See also==<br />
<br />
* [[Streaming components]]<br />
* [[Manager Worker Threads System]]<br />
* [[Array of threads]]<br />
[[Category:Tutorials]]</div>Mvampirehttps://wiki.freepascal.org/index.php?title=Multithreaded_Application_Tutorial&diff=44713Multithreaded Application Tutorial2010-09-07T21:17:36Z<p>Mvampire: /* See also */</p>
<hr />
<div>{{Multithreaded Application Tutorial}}<br />
<br />
== Overview ==<br />
This page will try to explain how to write and debug a multi-threaded application with Free Pascal and Lazarus. A multi-threaded application is one that creates two or more threads of execution that work at the same time. If you are new to multi-threading, please read the paragraph "Do you need multi-threading?" to determine whether it is really required; this may save you many headaches.<br />
<br />
One of the threads is called the Main Thread. The Main Thread is the one that is created by the Operating System once our application starts. The Main Thread '''must be''' the only thread that updates the components that interfaces with the user: otherwise, the application may hang.<br />
<br />
The main idea is that the application can do some processing in background in a second thread while the user can continue working using the main thread.<br />
<br />
Another use of threads is just to have a better responding application. If you create an application, and when the user presses a button the application starts processing a big job... and while processing, the screen stops responding, and gives the user the impression that the application is frozen, a poor or misleading impression will be created. If the big job runs in a second thread, the application keeps responding (almost) as if it were idle. In this case it is a good idea, before starting the thread, to disable the buttons of the form to avoid the user starting more than one thread for the job.<br />
<br />
Another use of multi-threading may be a server application that is able to respond to many clients at the same time.<br />
<br />
== Do you need multi-threading? ==<br />
<br />
If you are new to multi-threading and you only want to make your application more responsive while your application performs moderately long-running tasks, then multi-threading may be more than is required.<br />
Multi-threaded applications are always more difficult to debug and they are often much more complex; in many cases you don't need multi-threading. A single thread is enough. If you can split up the time-consuming task into several smaller chunks, then instead you should use '''Application.ProcessMessages'''. This method allows the LCL to handle all waiting messages and returns.<br />
The central idea is to call Application.ProcessMessages at regular intervals during the execution of a long-running task to determine whether the user has clicked on something, or a progress indicator must be repainted, and so on.<br />
<br />
For example: Reading a big file and process it. <br />
See examples/multithreading/singlethreadingexample1.lpi.<br />
<br />
Multi-threading is only needed for<br />
* blocking handles, like network communications<br />
* using multiple processors simultaneously (SMP)<br />
* algorithms and library calls that must be called through an API an as such cannot be split up into smaller parts.<br />
<br />
== The TThread Class ==<br />
<br />
The following example can be found in the examples/multithreading/ directory.<br />
<br />
To create a multi-threaded application, the easiest way is to use the TThread Class. This class permits the creation of an additional thread (alongside the main thread) in a simple way. Normally you are required to override only 2 methods: the Create constructor, and the Execute method.<br />
<br />
In the constructor, you will prepare the thread to run. You will set the initial values of the variables or properties you need. The original constructor of TThread requires a parameter called Suspended. As you might expect, setting Suspended = True will prevent the thread starting automatically after the creation. If Suspended = False, the thread will start running just after the creation. If the thread is created suspended, then it will run only after the Resume method is called.<br />
<br />
As of FPC version 2.0.1 and later, TThread.Create also has an implicit parameter for Stack Size. You can now change the default stack size of each thread you create if you need it. Deep procedure call recursions in a thread are a good example. If you don't specify the stack size parameter, a default OS stack size is used.<br />
<br />
In the overridden Execute method you will write the code that will run on the thread.<br />
<br />
The TThread class has one important property: <br />
Terminated : boolean;<br />
<br />
If the thread has a loop (and this is typical), the loop should be exited when Terminated is true (it is false by default). Within each pass, the value of Terminated must be checked, and if it is true then the loop should be exited as quickly as is appropriate, after any necessary cleanup. Bear in mind that the Terminate method does not do anything by default: the .Execute method must explicitly implement support for it to quit its job.<br />
<br />
As we explained earlier, the thread should not interact with the visible components. Updates to visible components must be made within the context of the main thread. To do this, a TThread method called Synchronize exists. Synchronize requires a method (that takes no parameters) as an argument. When you call that method through Synchronize(@MyMethod), the thread execution will be paused, the code of MyMethod will run in the main thread, and then the thread execution will be resumed. The exact working of Synchronize depends on the platform, but basically it does this: it posts a message onto the main message queue and goes to sleep. Eventually the main thread processes the message and calls MyMethod. This way MyMethod is called without context, that means not during a mouse down event or during paint event, but after. After the main thread executed MyMethod, it wakes the sleeping Thread and processes the next message. The Thread then continues.<br />
<br />
There is another important property of TThread: FreeOnTerminate. If this property is true, the thread object is automatically freed when the thread execution (.Execute method) stops. Otherwise the application will need to free it manually.<br />
<br />
Example:<br />
<br />
<delphi> Type<br />
TMyThread = class(TThread)<br />
private<br />
fStatusText : string;<br />
procedure ShowStatus;<br />
protected<br />
procedure Execute; override;<br />
public<br />
Constructor Create(CreateSuspended : boolean);<br />
end;<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.ShowStatus;<br />
// this method is executed by the mainthread and can therefore access all GUI elements.<br />
begin<br />
Form1.Caption := fStatusText;<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
newStatus : string;<br />
begin<br />
fStatusText := 'TMyThread Starting...';<br />
Synchronize(@Showstatus);<br />
fStatusText := 'TMyThread Running...';<br />
while (not Terminated) and ([any condition required]) do<br />
begin<br />
...<br />
[here goes the code of the main thread loop]<br />
...<br />
if NewStatus <> fStatusText then<br />
begin<br />
fStatusText := newStatus;<br />
Synchronize(@Showstatus);<br />
end;<br />
end;<br />
end;<br />
</delphi><br />
<br />
On the application,<br />
<br />
<delphi><br />
var<br />
MyThread : TMyThread;<br />
begin<br />
MyThread := TMyThread.Create(True); // This way it doesn't start automatically<br />
...<br />
[Here the code initialises anything required before the threads starts executing]<br />
...<br />
MyThread.Resume;<br />
end;</delphi><br />
<br />
If you want to make your application more flexible you can create an event for the thread; this way your synchronized method won't be tightly coupled with a specific form or class: you can attach listeners to the thread's event. Here is an example:<br />
<br />
<delphi> Type<br />
TShowStatusEvent = procedure(Status: String) of Object;<br />
<br />
TMyThread = class(TThread)<br />
private<br />
fStatusText : string;<br />
FOnShowStatus: TShowStatusEvent;<br />
procedure ShowStatus;<br />
protected<br />
procedure Execute; override;<br />
public<br />
Constructor Create(CreateSuspended : boolean);<br />
property OnShowStatus: TShowStatusEvent read FOnShowStatus write FOnShowStatus;<br />
end;<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.ShowStatus;<br />
// this method is executed by the mainthread and can therefore access all GUI elements.<br />
begin<br />
if Assigned(FOnShowStatus) then<br />
begin<br />
FOnShowStatus(fStatusText);<br />
end;<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
newStatus : string;<br />
begin<br />
fStatusText := 'TMyThread Starting...';<br />
Synchronize(@Showstatus);<br />
fStatusText := 'TMyThread Running...';<br />
while (not Terminated) and ([any condition required]) do<br />
begin<br />
...<br />
[here goes the code of the main thread loop]<br />
...<br />
if NewStatus <> fStatusText then<br />
begin<br />
fStatusText := newStatus;<br />
Synchronize(@Showstatus);<br />
end;<br />
end;<br />
end;<br />
</delphi><br />
<br />
On the application,<br />
<br />
<delphi> Type<br />
TForm1 = class(TForm)<br />
Button1: TButton;<br />
Label1: TLabel;<br />
procedure FormCreate(Sender: TObject);<br />
procedure FormDestroy(Sender: TObject);<br />
private<br />
{ private declarations }<br />
MyThread: TMyThread; <br />
procedure ShowStatus(Status: string);<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
procedure TForm1.FormCreate(Sender: TObject);<br />
begin<br />
inherited;<br />
MyThread := TMyThread.Create(true);<br />
MyThread.OnShowStatus := @ShowStatus;<br />
end;<br />
<br />
procedure TForm1.FormDestroy(Sender: TObject);<br />
begin<br />
MyThread.Terminate;<br />
MyThread.Free;<br />
inherited;<br />
end;<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
begin<br />
MyThread.Resume;<br />
end;<br />
<br />
procedure TForm1.ShowStatus(Status: string);<br />
begin<br />
Label1.Caption := Status;<br />
end;</delphi><br />
<br />
== Special things to take care of ==<br />
<br />
=== Stack checking under Windows ===<br />
<br />
There is a potential headache in Windows with Threads if you use the -Ct (stack check) switch.<br />
For reasons not so clear the stack check will "trigger" on any TThread.Create if you use the default stack size.<br />
The only work-around for the moment is to simply not use -Ct switch. Note that it does NOT cause an exception in<br />
the main thread, but in the newly created one. This "looks" like if the thread was never started.<br />
<br />
A good code to check for this and other exceptions which can occur in thread creation is:<br />
<br />
<delphi>MyThread := TThread.Create(False);<br />
if Assigned(MyThread.FatalException) then<br />
raise MyThread.FatalException;</delphi><br />
<br />
This code will assure that any exception which occurred during thread creation will be raised in your main thread.<br />
<br />
== Units needed for a multi-threaded application ==<br />
You don´t need any special unit for this to work with Windows.<br />
However with Linux, Mac OS X and FreeBSD, you need the cthreads unit and it ''must'' be the first used unit of the project (the program unit, .lpr)!<br />
<br />
So, your Lazarus application code should look like:<br />
<br />
<Delphi><br />
program MyMultiThreadedProgram;<br />
{$mode objfpc}{$H+}<br />
uses<br />
{$ifdef unix}<br />
cthreads,<br />
cmem, // the c memory manager is on some systems much faster for multi-threading<br />
{$endif}<br />
Interfaces, // this includes the LCL widgetset<br />
Forms<br />
{ you can add units here },<br />
</Delphi><br />
<br />
If you forget this and you use TThread you will get this error on startup:<br />
This binary has no thread support compiled in.<br />
Recompile the application with a thread-driver in the program uses clause before other units using thread.<br />
<br />
=== Multithreading in packages ===<br />
Packages which uses multi-threading should add the '''-dUseCThreads''' flag to the custom usage options. Open the package editor of the package, then Options > Usage > Custom and add ''-dUseCThreads''. This will define this flag to all projects and packages using this package, including the IDE. The IDE and all new applications created by the IDE have already the following code in their .lpr file:<br />
<Delphi><br />
uses<br />
{$IFDEF UNIX}{$IFDEF UseCThreads}<br />
cthreads,<br />
cmem, // the c memory manager is on some systems much faster for multi-threading<br />
{$ENDIF}{$ENDIF}<br />
</DELPHI><br />
<br />
===Heaptrc===<br />
<br />
You can not use the -gh switch with the ''cmem'' unit. The -gh switch uses the heaptrc unit, which extends the heap manager. Therefore the '''heaptrc''' unit must be used '''after''' the '''cmem''' unit.<br />
<br />
<Delphi>uses<br />
{$IFDEF UNIX}{$IFDEF UseCThreads}<br />
cthreads,<br />
cmem, // the c memory manager is on some systems much faster for multi-threading<br />
{$ENDIF}{$ENDIF}<br />
heaptrc,</DELPHI><br />
<br />
== SMP Support ==<br />
The good news is that if your application works properly multi-threaded this way, it is already SMP enabled!<br />
<br />
== Debugging Multi-threaded Applications with Lazarus ==<br />
The debugging on Lazarus requires GDB and is rapidly becoming more and more fully featured and stable. However, there still exists a few Linux distributions with some problems. <br />
<br />
=== Debugging output ===<br />
In a single threaded application, you can simply write to console/terminal/whatever and the order of the lines is the same as they were written.<br />
In multi-threaded application things are more complicated. If two threads are writing, say a line is written by thread A before a line by thread B, then the lines are not necessarily written in that order. It can even happen, that a thread writes its output, while the other thread is writing a line.<br />
<br />
The LCLProc unit contains several functions, to let each thread write to its own log file:<br />
<delphi> procedure DbgOutThreadLog(const Msg: string); overload;<br />
procedure DebuglnThreadLog(const Msg: string); overload;<br />
procedure DebuglnThreadLog(Args: array of const); overload;<br />
procedure DebuglnThreadLog; overload;</delphi><br />
<br />
For example:<br />
Instead of ''writeln('Some text ',123);'' use <br />
DebuglnThreadLog(['Some text ',123]);<br />
<br />
This will append a line 'Some text 123' to '''Log<PID>.txt''', where <PID> is the process ID of the current thread.<br />
<br />
It is a good idea to remove the log files before each run:<br />
rm -f Log* && ./project1<br />
<br />
=== Linux ===<br />
If you try to debug a multi-threaded application on Linux, you will have one big problem: the Desktop Manager on X server will hang. When that happens you can simply exit out of that session and create a new session by pressing CTRL+ALT+F3. That will give you a new session prompt. Once there enter sudo /etc/init.d/gdm restart. This will restart the desktop manager and get you back into your desktop.<br />
<br />
One method that solved this problem for Ubuntu x64 is to set the Project options for debugging required extra information file...<br />
<br />
Project Options -> Compiler Options -> Linking -> Debugging: Check Use external gdb debug symbols file (-Xg).<br />
<br />
If the above solution does not work a workaround is:<br />
<br />
Create a new instance of X with:<br />
<br />
X :1 &<br />
<br />
It will open, and when you switch to another desktop (the one you are working with pressing CTRL+ALT+F7), you will be able to go back to the new graphical desktop with CTRL+ALT+F8 (if this combination does not work, try with CTRL+ALT+F2... this one worked on [http://www.slackware.com Slackware]).<br />
<br />
Then you could, if you want, create a desktop session on the X started with:<br />
<br />
gnome-session --display=:1 &<br />
<br />
Then, in Lazarus, on the run parameters dialog for the project, check "Use display" and enter :1.<br />
<br />
Now the application will run on the second X server and you will be able to debug it on the first one.<br />
<br />
This was tested with Free Pascal 2.0 and Lazarus 0.9.10 on Windows and Linux.<br />
<br />
<br />
----<br />
<br />
Instead of creating a new X session, one can use [http://en.wikipedia.org/wiki/Xnest Xnest]. Xnest is a X session on a window. Using it X server didn't lock while debugging threads, and it's much easier to debug without keeping changing terminals.<br />
<br />
The command line to run Xnest is<br />
<br />
Xnest :1 -ac<br />
<br />
to create a X session on :1, and disabling access control.<br />
<br />
== Widgetsets ==<br />
The win32, the gtk and the carbon interfaces fully support multi-threading. This means, TThread, critical sections and Synchronize work.<br />
<br />
== Critical sections ==<br />
A ''critical section'' is an object used to make sure, that some part of the code is executed only by one thread at a time. A critical section needs to be created/initialized before it can be used and be freed when it is not needed anymore.<br />
<br />
Critical sections are normally used this way:<br />
<br />
Add the unit SyncObjs.<br />
<br />
Declare the section (globally for all threads which should access the section):<br />
MyCriticalSection: TRTLCriticalSection;<br />
<br />
Create the section:<br />
InitializeCriticalSection(MyCriticalSection);<br />
<br />
Run some threads. Doing something exclusively<br />
<delphi><br />
EnterCriticalSection(MyCriticalSection);<br />
try<br />
// access some variables, write files, send some network packets, etc<br />
finally<br />
LeaveCriticalSection(MyCriticalSection);<br />
end;</delphi><br />
<br />
After all threads terminated, free it:<br />
DeleteCriticalSection(MyCriticalSection);<br />
<br />
As an alternative, you can use a TCriticalSection object. The creation does the initialization, the Enter method does the EnterCriticalSection, the Leave method does the LeaveCriticalSection and the destruction of the object does the deletion.<br />
<br />
For example: 5 threads incrementing a counter.<br />
See lazarus/examples/multithreading/criticalsectionexample1.lpi<br />
<br />
'''BEWARE:''' There are two sets of the above 4 functions. The RTL and the LCL ones. The LCL ones are defined in the unit LCLIntf and LCLType. Both work pretty much the same. You can use both at the same time in your application, but you should not use a RTL function with an LCL Critical Section and vice versus.<br />
<br />
<br />
=== Sharing Variables ===<br />
If some threads share a variable, that is read only, then there is nothing to worry about. Just read it.<br />
But if one or several threads changes the variable, then you must make sure, that only one thread accesses the variables at a time.<br />
<br />
For example: 5 threads incrementing a counter.<br />
See lazarus/examples/multithreading/criticalsectionexample1.lpi<br />
<br />
== Waiting for another thread ==<br />
If a thread A needs a result of another thread B, it must wait, till B has finished. <br />
<br />
'''Important:''' The main thread should never wait for another thread. Instead use Synchronize (see above).<br />
<br />
See for an example: lazarus/examples/multithreading/waitforexample1.lpi<br />
<br />
<delphi>{ TThreadA }<br />
<br />
procedure TThreadA.Execute;<br />
begin<br />
Form1.ThreadB:=TThreadB.Create(false);<br />
// create event<br />
WaitForB:=RTLEventCreate;<br />
while not Application.Terminated do begin<br />
// wait infinitely (until B wakes A)<br />
RtlEventWaitFor(WaitForB);<br />
writeln('A: ThreadB.Counter='+IntToStr(Form1.ThreadB.Counter));<br />
end;<br />
end;<br />
<br />
{ TThreadB }<br />
<br />
procedure TThreadB.Execute;<br />
var<br />
i: Integer;<br />
begin<br />
Counter:=0;<br />
while not Application.Terminated do begin<br />
// B: Working ...<br />
Sleep(1500);<br />
inc(Counter);<br />
// wake A<br />
RtlEventSetEvent(Form1.ThreadA.WaitForB);<br />
end;<br />
end;</delphi><br />
<br />
Note: RtlEventSetEvent can be called before RtlEventWaitFor. Then RtlEventWaitFor will return immediately. Use RTLeventResetEvent to clear a flag.<br />
<br />
== Fork ==<br />
When forking in a multi-threaded application, be aware that any threads created and running BEFORE the fork (or fpFork) call, will NOT be running in the child process. As stated on the fork() man page, any threads that were running before the fork call, their state will be undefined.<br />
<br />
So be aware of any threads initializing before the call (including on the initialization section). They will NOT work.<br />
<br />
== Parallel procedures/loops ==<br />
A special case of multi threading is running a single procedure in parallel. See [[Parallel procedures]].<br />
<br />
== Distributed computing ==<br />
The next higher steps after multi threading is running the threads on multiple machines. <br />
* You can use one of the TCP suites like synapse, lnet or indy for communications. This gives you maximum flexibility and is mostly used for loosely connected Client / Server applications.<br />
* You can use message passing libraries like [[MPICH]], which are used for HPC (High Performance Computing) on clusters.<br />
<br />
<br />
<br />
== External threads ==<br />
To make Free Pascal's threading system to work properly, each newly created FPC thread needs to be initialized (more exactly, the exception, I/O system and threadvar system per thread needs to be initialized so threadvars and heap are working). That is fully automatically done for you if you use BeginThread (or indirectly by using the TThread class). However, if you use threads that were created without BeginThread (i.e. external threads), additional work (currently) might be required. External threads also include those that were created in external C libraries (.DLL/.so). <br />
<br />
<br />
Things to consider when using external threads (might not be needed in all or future compiler versions): <br />
<br />
* Do not use external threads at all - use FPC threads. If can you can get control over how the thread is created, create the thread by yourself by using BeginThread.<br />
<br />
If the calling convention doesn't fit (e.g. if your original thread function needs cdecl calling convention but BeginThread needs pascal convention, create a record, store the original required thread function in it, and call that function in your pascal thread function: <br />
<br />
<Delphi>type <br />
TCdeclThreadFunc = function (user_data:Pointer):Pointer;cdecl; <br />
<br />
PCdeclThreadFuncData = ^TCdeclThreadFunc; <br />
TCdeclThreadFuncData = record<br />
Func: TCdeclThreadFunc; //cdecl function<br />
Data: Pointer; //original data<br />
end; <br />
<br />
// The Pascal thread calls the cdecl function<br />
function C2P_Translator(FuncData: pointer) : ptrint;<br />
var<br />
ThreadData: TCdeclThreadFuncData;<br />
begin <br />
ThreadData := PCdeclThreadFuncData(FuncData)^;<br />
Result := ptrint(ThreadData.Func(ThreadData.Data));<br />
end; <br />
<br />
procedure CreatePascalThread;<br />
var<br />
ThreadData: PCdeclThreadFunc; <br />
begin <br />
New(ThreadData);<br />
// this is the desired cdecl thread function<br />
ThreadData^.Func := func; <br />
ThreadData^.Data := user_data; <br />
// this creates the Pascal thread<br />
BeginThread(@C2P_Translator, ThreadData );<br />
end;</Delphi><br />
<br />
<br />
* Initialize the FPC's threading system by creating a dummy thread. If you don't create any Pascal thread in your app, the thread system won't be initialized (and thus threadvars won't work and thus heap will not work correctly). <br />
<br />
<Delphi>type<br />
tc = class(tthread)<br />
procedure execute;override;<br />
end;<br />
<br />
procedure tc.execute;<br />
begin<br />
end;<br />
<br />
{ main program } <br />
begin<br />
{ initialise threading system }<br />
with tc.create(false) do<br />
begin<br />
waitfor;<br />
free;<br />
end;<br />
{ ... your code follows } <br />
end.</Delphi><br />
<br />
(After the threading system is initialized, the runtime may set the system variable "IsMultiThread" to true which is used by FPC routines to perform locks here and there. You should not set this variable manually.) <br />
<br />
<br />
* If for some reason this doesn't work for you, try this code in your external thread function:<br />
<br />
<Delphi>function ExternalThread(param: Pointer): LongInt; stdcall;<br />
var<br />
tm: TThreadManager;<br />
begin<br />
GetThreadManager(tm);<br />
tm.AllocateThreadVars;<br />
InitThread(1000000); // adjust inital stack size here<br />
<br />
{ do something threaded here ... }<br />
<br />
Result:=0;<br />
end;</Delphi><br />
<br />
<br />
=== Identifying external threads ===<br />
Sometimes you even don't know if you have to deal with external threads (e.g. if some C library makes a callback). This can help to analyse this: <br />
<br />
1. Ask the OS for the ID of the current thread at your application's start <br />
<br />
<Delphi>Win32: GetCurrentThreadID();<br />
Darwin: GetThreadID(); <br />
Linux: TThreadID(pthread_self);</Delphi><br />
<br />
2. Ask again for the ID of the current thread inside the thread function and compare this by the result of step 1.<br />
<br />
==See also==<br />
<br />
* [[Streaming components]]<br />
* [[Manager Worker Threads System]]<br />
* [[Example of multi-threaded application: array of threads]]<br />
[[Category:Tutorials]]</div>Mvampirehttps://wiki.freepascal.org/index.php?title=Example_of_multi-threaded_application:_array_of_threads&diff=44712Example of multi-threaded application: array of threads2010-09-07T21:15:54Z<p>Mvampire: </p>
<hr />
<div>Here I want to show an example how to create many threads and wait while they will not finish their jobs. I'm writing this tutorial because it was not obvious for me to write such a program after reading Multithread Application Tutorial. I was writing my application for Linux x86_64.<br />
<br />
Let's assume that we have the following loop:<br />
<delphi><br />
var results: integer;<br />
File: text;<br />
begin<br />
<br />
...<br />
<br />
for i:=1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
...<br />
</delphi><br />
This loop runs procedure that "calculate" some 'result'. After this we write this array to a text file... Now we want to divide this job to many parallel threads.<br />
<br />
To do this, You need to perform the following steps:<br />
<br />
1. Manage memory. Your threads can read any variables any time You need, but they shouldn't write any data to same variables (global) at the same time. So Your threads will not be able to write anything to one variable like 'result'. And they will not be able to write it to file. Moreover, I was interested to write my results to file in order (from 1 to n), and threads will not do this. So first step is to create global array of results (1 cell for 1 thread). If your procedure write something to many variables - you can create several arrays or array of record.<br />
<br />
I use a separate unit for global variables:<br />
<delphi><br />
unit Global;<br />
{$mode objfpc}{$H+}<br />
interface<br />
uses Classes, SysUtils; <br />
<br />
var result: array [1..100] of threads; //We will create not more than 100 threads.<br />
<br />
implementation<br />
end.<br />
</delphi><br />
2. Add threads class. I also use a separate unit for it. So I need to describe 'property Terminated' as the public (to use it from the main program).<br />
<delphi><br />
unit MyThreads;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils; <br />
<br />
Type<br />
TMyThread = class(TThread)<br />
private<br />
protected<br />
procedure Execute; override;<br />
public<br />
start,finish: integer; //I will initialise these fields before running the thread. So each thread will receive a part of task.<br />
property Terminated;<br />
Constructor Create(CreateSuspended : boolean);<br />
end;<br />
<br />
implementation<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
i: integer;<br />
begin<br />
i:=start;<br />
while (not Terminated) and (i<=finish) do<br />
begin<br />
MyProcedure(result[i]);<br />
inc(i);<br />
if i=finish+1 then Terminate;<br />
end;<br />
end;<br />
end. <br />
</delphi><br />
3. Rewrite main program.<br />
<br />
You need to add 'cthreads' to the main unit, not to unit with threads!<br />
<br />
Tips: in my Lazarus IDE I was not able to debug multi-threading applications if I don't use 'pthreads'. I have red that if you use 'cmem', the program works faster, but I strongly recommend you to check it for any particular case (my program hangs when I use 'cmem').<br />
<delphi><br />
uses cthreads,<br />
// cmem,<br />
// pthreads,<br />
Classes, SysUtils, CustApp, MyThreads, Global<br />
<br />
var<br />
ThreadArray: array [1..100] of TMyThread; //We will create no more than 100 threads.<br />
i, number_of_threads: integer;<br />
begin<br />
<br />
number of threads:=100;<br />
<br />
while n div number_of_threads <1 do dec(number_of_threads); //If n<100 then we don't have enough tasks for 100 threads.<br />
<br />
for i:=1 to number_of_threads do begin<br />
ThreadArray[i]:= TMyThread.Create(True);<br />
ThreadArray[i].start:=(i-1)*(n div number_of_threads)+1;;<br />
ThreadArray[i].finish:=i*(n div number_of_threads);<br />
ThreadArray[i].Resume;<br />
end;<br />
<br />
for i:=1 to 10 do if not ThreadArray[i].Terminated then Sleep(10); //Waiting while threads will not finish their job.<br />
<br />
for i:=1 to n do Writeln(file,result[i]); //Writing results in order.<br />
<br />
//Now we should finish part of the tast that couldn't be divided to 100 threads.<br />
//For examle, if n=10050 then 10000 will be divided to 100 threads. And last 50 (< 100) we should finish in non-parallel mode<br />
//That's how I do this. Instead, You can write something like: 'if i=number_of_threads then ThreadArray[i].finish:=n;'<br />
//In this case last thread will finish the job.<br />
<br />
if n mod number_of_threads<>0 then<br />
for i:=(n div number_of_threads) + 1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
Terminate;<br />
end;<br />
</delphi></div>Mvampirehttps://wiki.freepascal.org/index.php?title=Example_of_multi-threaded_application:_array_of_threads&diff=44711Example of multi-threaded application: array of threads2010-09-07T21:14:13Z<p>Mvampire: </p>
<hr />
<div>Here I want to show an example how to create many threads and wait while they will not finish their jobs. I'm writing this tutorial because it was not obvious for me to write such a program after reading Multithread Application Tutorial. I was writing my application for Linux x86_64.<br />
<br />
Let's assume that we have the following loop:<br />
<delphi><br />
var results: integer;<br />
File: text;<br />
begin<br />
<br />
...<br />
<br />
for i:=1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
...<br />
</delphi><br />
This loop runs procedure that "calculate" some 'result'. After this we write this array to a text file... Now we want to divide this job to many parallel threads.<br />
<br />
To do this, You need to perform the following steps:<br />
<br />
1. Manage memory. Your threads can read any variables any time You need, but they shouldn't write any data to same variables (global) at the same time. So Your threads will not be able to write anything to one variable like 'result'. And they will not be able to write it to file. Moreover, I was interested to write my results to file in order (from 1 to n), and threads will not do this. So first step is to create global array of results (1 cell for 1 thread). If your procedure write something to many variables - you can create several arrays or array of record.<br />
<br />
I use a separate unit for global variables:<br />
<delphi><br />
unit Global;<br />
{$mode objfpc}{$H+}<br />
interface<br />
uses Classes, SysUtils; <br />
<br />
var result: array [1..100] of threads; //We will create not more than 100 threads.<br />
<br />
implementation<br />
end.<br />
</delphi><br />
2. Add threads class. I also use a separate unit for it. So I need to describe 'property Terminated' as the public (to use it from the main program).<br />
<delphi><br />
unit MyThreads;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils; <br />
<br />
Type<br />
TMyThread = class(TThread)<br />
private<br />
protected<br />
procedure Execute; override;<br />
public<br />
start,finish: integer; //I will initialise these fields before running the thread. So each thread will receive a part of task.<br />
property Terminated;<br />
Constructor Create(CreateSuspended : boolean);<br />
end;<br />
<br />
implementation<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
i: integer;<br />
begin<br />
i:=start;<br />
while (not Terminated) and (i<=finish) do<br />
begin<br />
MyProcedure(result[i]);<br />
inc(i);<br />
if i=finish+1 then Terminate;<br />
end;<br />
end;<br />
end. <br />
</delphi><br />
3. Rewrite main program.<br />
<br />
You need to add 'cthreads' to the main unit, not to unit with threads!<br />
Tips: in my Lazarus IDE I was not able to debug multi-threading applications if I don't use 'pthreads'. I have red that if you use 'cmem', the program works faster, but I strongly recommend you to check it for any particular case (my program hangs when I use 'cmem').<br />
<delphi><br />
uses cthreads,<br />
// cmem,<br />
// pthreads,<br />
Classes, SysUtils, CustApp, MyThreads, Global<br />
<br />
var<br />
ThreadArray: array [1..100] of TMyThread; //We will create no more than 100 threads.<br />
i, number_of_threads: integer;<br />
begin<br />
<br />
number of threads:=100;<br />
<br />
while n div number_of_threads <1 do dec(number_of_threads); //If n<100 then we don't have enough tasks for 100 threads.<br />
<br />
for i:=1 to number_of_threads do begin<br />
ThreadArray[i]:= TMyThread.Create(True);<br />
ThreadArray[i].start:=(i-1)*(n div number_of_threads)+1;;<br />
ThreadArray[i].finish:=i*(n div number_of_threads);<br />
ThreadArray[i].Resume;<br />
end;<br />
<br />
for i:=1 to 10 do if not ThreadArray[i].Terminated then Sleep(10); //Waiting while threads will not finish their job.<br />
<br />
for i:=1 to n do Writeln(file,result[i]); //Writing results in order.<br />
<br />
//Now we should finish part of the tast that couldn't be divided to 100 threads.<br />
//For examle, if n=10050 then 10000 will be divided to 100 threads. And last 50 (< 100) we should finish in non-parallel mode<br />
//That's how I do this. Instead, You can write something like: 'if i=number_of_threads then ThreadArray[i].finish:=n;'<br />
//In this case last thread will finish the job.<br />
<br />
if n mod number_of_threads<>0 then<br />
for i:=(n div number_of_threads) + 1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
Terminate;<br />
end;<br />
</delphi></div>Mvampirehttps://wiki.freepascal.org/index.php?title=Example_of_multi-threaded_application:_array_of_threads&diff=44710Example of multi-threaded application: array of threads2010-09-07T21:11:17Z<p>Mvampire: /* Example of multithreading application: array of threads */</p>
<hr />
<div>Here I want to show an example how to create many threads and wait while they will not finish their jobs. I'm writing this tutorial because it was not obvious for me to write such a program after reading Multithread Application Tutorial. I was writing my application for Linux x86_64.<br />
<br />
Let's assume that we have the following loop:<br />
<br />
var results: integer;<br />
File: text;<br />
begin<br />
<br />
...<br />
<br />
for i:=1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
...<br />
<br />
This loop runs procedure that "calculate" some 'result'. After this we write this array to a text file... Now we want to divide this job to many parallel threads.<br />
<br />
To do this, You need to perform the following steps:<br />
<br />
1. Manage memory. Your threads can read any variables any time You need, but they shouldn't write any data to same variables (global) at the same time. So Your threads will not be able to write anything to one variable like 'result'. And they will not be able to write it to file. Moreover, I was interested to write my results to file in order (from 1 to n), and threads will not do this. So first step is to create global array of results (1 cell for 1 thread). If your procedure write something to many variables - you can create several arrays or array of record.<br />
<br />
I use a separate unit for global variables:<br />
<br />
unit Global;<br />
{$mode objfpc}{$H+}<br />
interface<br />
uses Classes, SysUtils; <br />
<br />
var result: array [1..100] of threads; //We will create not more than 100 threads.<br />
<br />
implementation<br />
end.<br />
<br />
2. Add threads class. I also use a separate unit for it. So I need to describe 'property Terminated' as the public (to use it from the main program).<br />
<br />
unit MyThreads;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils; <br />
<br />
Type<br />
TMyThread = class(TThread)<br />
private<br />
protected<br />
procedure Execute; override;<br />
public<br />
start,finish: integer; //I will initialise these fields before running the thread. So each thread will receive a part of task.<br />
property Terminated;<br />
Constructor Create(CreateSuspended : boolean);<br />
end;<br />
<br />
implementation<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
i: integer;<br />
begin<br />
i:=start;<br />
while (not Terminated) and (i<=finish) do<br />
begin<br />
MyProcedure(result[i]);<br />
inc(i);<br />
if i=finish+1 then Terminate;<br />
end;<br />
end;<br />
end. <br />
<br />
3. Rewrite main program.<br />
<br />
You need to add 'cthreads' to the main unit, not to unit with threads!<br />
Tips: in my Lazarus IDE I was not able to debug multi-threading applications if I don't use 'pthreads'. I have red that if you use 'cmem', the program works faster, but I strongly recommend you to check it for any particular case (my program hangs when I use 'cmem').<br />
<br />
uses cthreads,<br />
// cmem,<br />
// pthreads,<br />
Classes, SysUtils, CustApp, MyThreads, Global<br />
<br />
var<br />
ThreadArray: array [1..100] of TMyThread; //We will create no more than 100 threads.<br />
i, number_of_threads: integer;<br />
begin<br />
<br />
number of threads:=100;<br />
<br />
while n div number_of_threads <1 do dec(number_of_threads); //If n<100 then we don't have enough tasks for 100 threads.<br />
<br />
for i:=1 to number_of_threads do begin<br />
ThreadArray[i]:= TMyThread.Create(True);<br />
ThreadArray[i].start:=(i-1)*(n div number_of_threads)+1;;<br />
ThreadArray[i].finish:=i*(n div number_of_threads);<br />
ThreadArray[i].Resume;<br />
end;<br />
<br />
for i:=1 to 10 do if not ThreadArray[i].Terminated then Sleep(10); //Waiting while threads will not finish their job.<br />
<br />
for i:=1 to n do Writeln(file,result[i]); //Writing results in order.<br />
<br />
//Now we should finish part of the tast that couldn't be divided to 100 threads.<br />
//For examle, if n=10050 then 10000 will be divided to 100 threads. And last 50 (< 100) we should finish in non-parallel mode<br />
//That's how I do this. Instead, You can write something like: 'if i=number_of_threads then ThreadArray[i].finish:=n;'<br />
//In this case last thread will finish the job.<br />
<br />
if n mod number_of_threads<>0 then<br />
for i:=(n div number_of_threads) + 1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
Terminate;<br />
end;</div>Mvampirehttps://wiki.freepascal.org/index.php?title=Example_of_multi-threaded_application:_array_of_threads&diff=44709Example of multi-threaded application: array of threads2010-09-07T21:10:58Z<p>Mvampire: New page: = Example of multithreading application: array of threads = Here I want to show an example how to create many threads and wait while they will not finish their jobs. I'm writing this tuto...</p>
<hr />
<div>= Example of multithreading application: array of threads =<br />
<br />
Here I want to show an example how to create many threads and wait while they will not finish their jobs. I'm writing this tutorial because it was not obvious for me to write such a program after reading Multithread Application Tutorial. I was writing my application for Linux x86_64.<br />
<br />
Let's assume that we have the following loop:<br />
<br />
var results: integer;<br />
File: text;<br />
begin<br />
<br />
...<br />
<br />
for i:=1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
...<br />
<br />
This loop runs procedure that "calculate" some 'result'. After this we write this array to a text file... Now we want to divide this job to many parallel threads.<br />
<br />
To do this, You need to perform the following steps:<br />
<br />
1. Manage memory. Your threads can read any variables any time You need, but they shouldn't write any data to same variables (global) at the same time. So Your threads will not be able to write anything to one variable like 'result'. And they will not be able to write it to file. Moreover, I was interested to write my results to file in order (from 1 to n), and threads will not do this. So first step is to create global array of results (1 cell for 1 thread). If your procedure write something to many variables - you can create several arrays or array of record.<br />
<br />
I use a separate unit for global variables:<br />
<br />
unit Global;<br />
{$mode objfpc}{$H+}<br />
interface<br />
uses Classes, SysUtils; <br />
<br />
var result: array [1..100] of threads; //We will create not more than 100 threads.<br />
<br />
implementation<br />
end.<br />
<br />
2. Add threads class. I also use a separate unit for it. So I need to describe 'property Terminated' as the public (to use it from the main program).<br />
<br />
unit MyThreads;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils; <br />
<br />
Type<br />
TMyThread = class(TThread)<br />
private<br />
protected<br />
procedure Execute; override;<br />
public<br />
start,finish: integer; //I will initialise these fields before running the thread. So each thread will receive a part of task.<br />
property Terminated;<br />
Constructor Create(CreateSuspended : boolean);<br />
end;<br />
<br />
implementation<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
i: integer;<br />
begin<br />
i:=start;<br />
while (not Terminated) and (i<=finish) do<br />
begin<br />
MyProcedure(result[i]);<br />
inc(i);<br />
if i=finish+1 then Terminate;<br />
end;<br />
end;<br />
end. <br />
<br />
3. Rewrite main program.<br />
<br />
You need to add 'cthreads' to the main unit, not to unit with threads!<br />
Tips: in my Lazarus IDE I was not able to debug multi-threading applications if I don't use 'pthreads'. I have red that if you use 'cmem', the program works faster, but I strongly recommend you to check it for any particular case (my program hangs when I use 'cmem').<br />
<br />
uses cthreads,<br />
// cmem,<br />
// pthreads,<br />
Classes, SysUtils, CustApp, MyThreads, Global<br />
<br />
var<br />
ThreadArray: array [1..100] of TMyThread; //We will create no more than 100 threads.<br />
i, number_of_threads: integer;<br />
begin<br />
<br />
number of threads:=100;<br />
<br />
while n div number_of_threads <1 do dec(number_of_threads); //If n<100 then we don't have enough tasks for 100 threads.<br />
<br />
for i:=1 to number_of_threads do begin<br />
ThreadArray[i]:= TMyThread.Create(True);<br />
ThreadArray[i].start:=(i-1)*(n div number_of_threads)+1;;<br />
ThreadArray[i].finish:=i*(n div number_of_threads);<br />
ThreadArray[i].Resume;<br />
end;<br />
<br />
for i:=1 to 10 do if not ThreadArray[i].Terminated then Sleep(10); //Waiting while threads will not finish their job.<br />
<br />
for i:=1 to n do Writeln(file,result[i]); //Writing results in order.<br />
<br />
//Now we should finish part of the tast that couldn't be divided to 100 threads.<br />
//For examle, if n=10050 then 10000 will be divided to 100 threads. And last 50 (< 100) we should finish in non-parallel mode<br />
//That's how I do this. Instead, You can write something like: 'if i=number_of_threads then ThreadArray[i].finish:=n;'<br />
//In this case last thread will finish the job.<br />
<br />
if n mod number_of_threads<>0 then<br />
for i:=(n div number_of_threads) + 1 to n do begin<br />
MyProcedure(result);<br />
Writeln(File,result);<br />
end;<br />
<br />
Terminate;<br />
end;</div>Mvampirehttps://wiki.freepascal.org/index.php?title=Multithreaded_Application_Tutorial&diff=44708Multithreaded Application Tutorial2010-09-07T20:21:47Z<p>Mvampire: /* See also */</p>
<hr />
<div>{{Multithreaded Application Tutorial}}<br />
<br />
== Overview ==<br />
This page will try to explain how to write and debug a multi-threaded application with Free Pascal and Lazarus. A multi-threaded application is one that creates two or more threads of execution that work at the same time. If you are new to multi-threading, please read the paragraph "Do you need multi-threading?" to determine whether it is really required; this may save you many headaches.<br />
<br />
One of the threads is called the Main Thread. The Main Thread is the one that is created by the Operating System once our application starts. The Main Thread '''must be''' the only thread that updates the components that interfaces with the user: otherwise, the application may hang.<br />
<br />
The main idea is that the application can do some processing in background in a second thread while the user can continue working using the main thread.<br />
<br />
Another use of threads is just to have a better responding application. If you create an application, and when the user presses a button the application starts processing a big job... and while processing, the screen stops responding, and gives the user the impression that the application is frozen, a poor or misleading impression will be created. If the big job runs in a second thread, the application keeps responding (almost) as if it were idle. In this case it is a good idea, before starting the thread, to disable the buttons of the form to avoid the user starting more than one thread for the job.<br />
<br />
Another use of multi-threading may be a server application that is able to respond to many clients at the same time.<br />
<br />
== Do you need multi-threading? ==<br />
<br />
If you are new to multi-threading and you only want to make your application more responsive while your application performs moderately long-running tasks, then multi-threading may be more than is required.<br />
Multi-threaded applications are always more difficult to debug and they are often much more complex; in many cases you don't need multi-threading. A single thread is enough. If you can split up the time-consuming task into several smaller chunks, then instead you should use '''Application.ProcessMessages'''. This method allows the LCL to handle all waiting messages and returns.<br />
The central idea is to call Application.ProcessMessages at regular intervals during the execution of a long-running task to determine whether the user has clicked on something, or a progress indicator must be repainted, and so on.<br />
<br />
For example: Reading a big file and process it. <br />
See examples/multithreading/singlethreadingexample1.lpi.<br />
<br />
Multi-threading is only needed for<br />
* blocking handles, like network communications<br />
* using multiple processors simultaneously (SMP)<br />
* algorithms and library calls that must be called through an API an as such cannot be split up into smaller parts.<br />
<br />
== The TThread Class ==<br />
<br />
The following example can be found in the examples/multithreading/ directory.<br />
<br />
To create a multi-threaded application, the easiest way is to use the TThread Class. This class permits the creation of an additional thread (alongside the main thread) in a simple way. Normally you are required to override only 2 methods: the Create constructor, and the Execute method.<br />
<br />
In the constructor, you will prepare the thread to run. You will set the initial values of the variables or properties you need. The original constructor of TThread requires a parameter called Suspended. As you might expect, setting Suspended = True will prevent the thread starting automatically after the creation. If Suspended = False, the thread will start running just after the creation. If the thread is created suspended, then it will run only after the Resume method is called.<br />
<br />
As of FPC version 2.0.1 and later, TThread.Create also has an implicit parameter for Stack Size. You can now change the default stack size of each thread you create if you need it. Deep procedure call recursions in a thread are a good example. If you don't specify the stack size parameter, a default OS stack size is used.<br />
<br />
In the overridden Execute method you will write the code that will run on the thread.<br />
<br />
The TThread class has one important property: <br />
Terminated : boolean;<br />
<br />
If the thread has a loop (and this is typical), the loop should be exited when Terminated is true (it is false by default). Within each pass, the value of Terminated must be checked, and if it is true then the loop should be exited as quickly as is appropriate, after any necessary cleanup. Bear in mind that the Terminate method does not do anything by default: the .Execute method must explicitly implement support for it to quit its job.<br />
<br />
As we explained earlier, the thread should not interact with the visible components. Updates to visible components must be made within the context of the main thread. To do this, a TThread method called Synchronize exists. Synchronize requires a method (that takes no parameters) as an argument. When you call that method through Synchronize(@MyMethod), the thread execution will be paused, the code of MyMethod will run in the main thread, and then the thread execution will be resumed. The exact working of Synchronize depends on the platform, but basically it does this: it posts a message onto the main message queue and goes to sleep. Eventually the main thread processes the message and calls MyMethod. This way MyMethod is called without context, that means not during a mouse down event or during paint event, but after. After the main thread executed MyMethod, it wakes the sleeping Thread and processes the next message. The Thread then continues.<br />
<br />
There is another important property of TThread: FreeOnTerminate. If this property is true, the thread object is automatically freed when the thread execution (.Execute method) stops. Otherwise the application will need to free it manually.<br />
<br />
Example:<br />
<br />
<delphi> Type<br />
TMyThread = class(TThread)<br />
private<br />
fStatusText : string;<br />
procedure ShowStatus;<br />
protected<br />
procedure Execute; override;<br />
public<br />
Constructor Create(CreateSuspended : boolean);<br />
end;<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.ShowStatus;<br />
// this method is executed by the mainthread and can therefore access all GUI elements.<br />
begin<br />
Form1.Caption := fStatusText;<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
newStatus : string;<br />
begin<br />
fStatusText := 'TMyThread Starting...';<br />
Synchronize(@Showstatus);<br />
fStatusText := 'TMyThread Running...';<br />
while (not Terminated) and ([any condition required]) do<br />
begin<br />
...<br />
[here goes the code of the main thread loop]<br />
...<br />
if NewStatus <> fStatusText then<br />
begin<br />
fStatusText := newStatus;<br />
Synchronize(@Showstatus);<br />
end;<br />
end;<br />
end;<br />
</delphi><br />
<br />
On the application,<br />
<br />
<delphi><br />
var<br />
MyThread : TMyThread;<br />
begin<br />
MyThread := TMyThread.Create(True); // This way it doesn't start automatically<br />
...<br />
[Here the code initialises anything required before the threads starts executing]<br />
...<br />
MyThread.Resume;<br />
end;</delphi><br />
<br />
If you want to make your application more flexible you can create an event for the thread; this way your synchronized method won't be tightly coupled with a specific form or class: you can attach listeners to the thread's event. Here is an example:<br />
<br />
<delphi> Type<br />
TShowStatusEvent = procedure(Status: String) of Object;<br />
<br />
TMyThread = class(TThread)<br />
private<br />
fStatusText : string;<br />
FOnShowStatus: TShowStatusEvent;<br />
procedure ShowStatus;<br />
protected<br />
procedure Execute; override;<br />
public<br />
Constructor Create(CreateSuspended : boolean);<br />
property OnShowStatus: TShowStatusEvent read FOnShowStatus write FOnShowStatus;<br />
end;<br />
<br />
constructor TMyThread.Create(CreateSuspended : boolean);<br />
begin<br />
FreeOnTerminate := True;<br />
inherited Create(CreateSuspended);<br />
end;<br />
<br />
procedure TMyThread.ShowStatus;<br />
// this method is executed by the mainthread and can therefore access all GUI elements.<br />
begin<br />
if Assigned(FOnShowStatus) then<br />
begin<br />
FOnShowStatus(fStatusText);<br />
end;<br />
end;<br />
<br />
procedure TMyThread.Execute;<br />
var<br />
newStatus : string;<br />
begin<br />
fStatusText := 'TMyThread Starting...';<br />
Synchronize(@Showstatus);<br />
fStatusText := 'TMyThread Running...';<br />
while (not Terminated) and ([any condition required]) do<br />
begin<br />
...<br />
[here goes the code of the main thread loop]<br />
...<br />
if NewStatus <> fStatusText then<br />
begin<br />
fStatusText := newStatus;<br />
Synchronize(@Showstatus);<br />
end;<br />
end;<br />
end;<br />
</delphi><br />
<br />
On the application,<br />
<br />
<delphi> Type<br />
TForm1 = class(TForm)<br />
Button1: TButton;<br />
Label1: TLabel;<br />
procedure FormCreate(Sender: TObject);<br />
procedure FormDestroy(Sender: TObject);<br />
private<br />
{ private declarations }<br />
MyThread: TMyThread; <br />
procedure ShowStatus(Status: string);<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
procedure TForm1.FormCreate(Sender: TObject);<br />
begin<br />
inherited;<br />
MyThread := TMyThread.Create(true);<br />
MyThread.OnShowStatus := @ShowStatus;<br />
end;<br />
<br />
procedure TForm1.FormDestroy(Sender: TObject);<br />
begin<br />
MyThread.Terminate;<br />
MyThread.Free;<br />
inherited;<br />
end;<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
begin<br />
MyThread.Resume;<br />
end;<br />
<br />
procedure TForm1.ShowStatus(Status: string);<br />
begin<br />
Label1.Caption := Status;<br />
end;</delphi><br />
<br />
== Special things to take care of ==<br />
<br />
=== Stack checking under Windows ===<br />
<br />
There is a potential headache in Windows with Threads if you use the -Ct (stack check) switch.<br />
For reasons not so clear the stack check will "trigger" on any TThread.Create if you use the default stack size.<br />
The only work-around for the moment is to simply not use -Ct switch. Note that it does NOT cause an exception in<br />
the main thread, but in the newly created one. This "looks" like if the thread was never started.<br />
<br />
A good code to check for this and other exceptions which can occur in thread creation is:<br />
<br />
<delphi>MyThread := TThread.Create(False);<br />
if Assigned(MyThread.FatalException) then<br />
raise MyThread.FatalException;</delphi><br />
<br />
This code will assure that any exception which occurred during thread creation will be raised in your main thread.<br />
<br />
== Units needed for a multi-threaded application ==<br />
You don´t need any special unit for this to work with Windows.<br />
However with Linux, Mac OS X and FreeBSD, you need the cthreads unit and it ''must'' be the first used unit of the project (the program unit, .lpr)!<br />
<br />
So, your Lazarus application code should look like:<br />
<br />
<Delphi><br />
program MyMultiThreadedProgram;<br />
{$mode objfpc}{$H+}<br />
uses<br />
{$ifdef unix}<br />
cthreads,<br />
cmem, // the c memory manager is on some systems much faster for multi-threading<br />
{$endif}<br />
Interfaces, // this includes the LCL widgetset<br />
Forms<br />
{ you can add units here },<br />
</Delphi><br />
<br />
If you forget this and you use TThread you will get this error on startup:<br />
This binary has no thread support compiled in.<br />
Recompile the application with a thread-driver in the program uses clause before other units using thread.<br />
<br />
=== Multithreading in packages ===<br />
Packages which uses multi-threading should add the '''-dUseCThreads''' flag to the custom usage options. Open the package editor of the package, then Options > Usage > Custom and add ''-dUseCThreads''. This will define this flag to all projects and packages using this package, including the IDE. The IDE and all new applications created by the IDE have already the following code in their .lpr file:<br />
<Delphi><br />
uses<br />
{$IFDEF UNIX}{$IFDEF UseCThreads}<br />
cthreads,<br />
cmem, // the c memory manager is on some systems much faster for multi-threading<br />
{$ENDIF}{$ENDIF}<br />
</DELPHI><br />
<br />
===Heaptrc===<br />
<br />
You can not use the -gh switch with the ''cmem'' unit. The -gh switch uses the heaptrc unit, which extends the heap manager. Therefore the '''heaptrc''' unit must be used '''after''' the '''cmem''' unit.<br />
<br />
<Delphi>uses<br />
{$IFDEF UNIX}{$IFDEF UseCThreads}<br />
cthreads,<br />
cmem, // the c memory manager is on some systems much faster for multi-threading<br />
{$ENDIF}{$ENDIF}<br />
heaptrc,</DELPHI><br />
<br />
== SMP Support ==<br />
The good news is that if your application works properly multi-threaded this way, it is already SMP enabled!<br />
<br />
== Debugging Multi-threaded Applications with Lazarus ==<br />
The debugging on Lazarus requires GDB and is rapidly becoming more and more fully featured and stable. However, there still exists a few Linux distributions with some problems. <br />
<br />
=== Debugging output ===<br />
In a single threaded application, you can simply write to console/terminal/whatever and the order of the lines is the same as they were written.<br />
In multi-threaded application things are more complicated. If two threads are writing, say a line is written by thread A before a line by thread B, then the lines are not necessarily written in that order. It can even happen, that a thread writes its output, while the other thread is writing a line.<br />
<br />
The LCLProc unit contains several functions, to let each thread write to its own log file:<br />
<delphi> procedure DbgOutThreadLog(const Msg: string); overload;<br />
procedure DebuglnThreadLog(const Msg: string); overload;<br />
procedure DebuglnThreadLog(Args: array of const); overload;<br />
procedure DebuglnThreadLog; overload;</delphi><br />
<br />
For example:<br />
Instead of ''writeln('Some text ',123);'' use <br />
DebuglnThreadLog(['Some text ',123]);<br />
<br />
This will append a line 'Some text 123' to '''Log<PID>.txt''', where <PID> is the process ID of the current thread.<br />
<br />
It is a good idea to remove the log files before each run:<br />
rm -f Log* && ./project1<br />
<br />
=== Linux ===<br />
If you try to debug a multi-threaded application on Linux, you will have one big problem: the Desktop Manager on X server will hang. When that happens you can simply exit out of that session and create a new session by pressing CTRL+ALT+F3. That will give you a new session prompt. Once there enter sudo /etc/init.d/gdm restart. This will restart the desktop manager and get you back into your desktop.<br />
<br />
One method that solved this problem for Ubuntu x64 is to set the Project options for debugging required extra information file...<br />
<br />
Project Options -> Compiler Options -> Linking -> Debugging: Check Use external gdb debug symbols file (-Xg).<br />
<br />
If the above solution does not work a workaround is:<br />
<br />
Create a new instance of X with:<br />
<br />
X :1 &<br />
<br />
It will open, and when you switch to another desktop (the one you are working with pressing CTRL+ALT+F7), you will be able to go back to the new graphical desktop with CTRL+ALT+F8 (if this combination does not work, try with CTRL+ALT+F2... this one worked on [http://www.slackware.com Slackware]).<br />
<br />
Then you could, if you want, create a desktop session on the X started with:<br />
<br />
gnome-session --display=:1 &<br />
<br />
Then, in Lazarus, on the run parameters dialog for the project, check "Use display" and enter :1.<br />
<br />
Now the application will run on the second X server and you will be able to debug it on the first one.<br />
<br />
This was tested with Free Pascal 2.0 and Lazarus 0.9.10 on Windows and Linux.<br />
<br />
<br />
----<br />
<br />
Instead of creating a new X session, one can use [http://en.wikipedia.org/wiki/Xnest Xnest]. Xnest is a X session on a window. Using it X server didn't lock while debugging threads, and it's much easier to debug without keeping changing terminals.<br />
<br />
The command line to run Xnest is<br />
<br />
Xnest :1 -ac<br />
<br />
to create a X session on :1, and disabling access control.<br />
<br />
== Widgetsets ==<br />
The win32, the gtk and the carbon interfaces fully support multi-threading. This means, TThread, critical sections and Synchronize work.<br />
<br />
== Critical sections ==<br />
A ''critical section'' is an object used to make sure, that some part of the code is executed only by one thread at a time. A critical section needs to be created/initialized before it can be used and be freed when it is not needed anymore.<br />
<br />
Critical sections are normally used this way:<br />
<br />
Add the unit SyncObjs.<br />
<br />
Declare the section (globally for all threads which should access the section):<br />
MyCriticalSection: TRTLCriticalSection;<br />
<br />
Create the section:<br />
InitializeCriticalSection(MyCriticalSection);<br />
<br />
Run some threads. Doing something exclusively<br />
<delphi><br />
EnterCriticalSection(MyCriticalSection);<br />
try<br />
// access some variables, write files, send some network packets, etc<br />
finally<br />
LeaveCriticalSection(MyCriticalSection);<br />
end;</delphi><br />
<br />
After all threads terminated, free it:<br />
DeleteCriticalSection(MyCriticalSection);<br />
<br />
As an alternative, you can use a TCriticalSection object. The creation does the initialization, the Enter method does the EnterCriticalSection, the Leave method does the LeaveCriticalSection and the destruction of the object does the deletion.<br />
<br />
For example: 5 threads incrementing a counter.<br />
See lazarus/examples/multithreading/criticalsectionexample1.lpi<br />
<br />
'''BEWARE:''' There are two sets of the above 4 functions. The RTL and the LCL ones. The LCL ones are defined in the unit LCLIntf and LCLType. Both work pretty much the same. You can use both at the same time in your application, but you should not use a RTL function with an LCL Critical Section and vice versus.<br />
<br />
<br />
=== Sharing Variables ===<br />
If some threads share a variable, that is read only, then there is nothing to worry about. Just read it.<br />
But if one or several threads changes the variable, then you must make sure, that only one thread accesses the variables at a time.<br />
<br />
For example: 5 threads incrementing a counter.<br />
See lazarus/examples/multithreading/criticalsectionexample1.lpi<br />
<br />
== Waiting for another thread ==<br />
If a thread A needs a result of another thread B, it must wait, till B has finished. <br />
<br />
'''Important:''' The main thread should never wait for another thread. Instead use Synchronize (see above).<br />
<br />
See for an example: lazarus/examples/multithreading/waitforexample1.lpi<br />
<br />
<delphi>{ TThreadA }<br />
<br />
procedure TThreadA.Execute;<br />
begin<br />
Form1.ThreadB:=TThreadB.Create(false);<br />
// create event<br />
WaitForB:=RTLEventCreate;<br />
while not Application.Terminated do begin<br />
// wait infinitely (until B wakes A)<br />
RtlEventWaitFor(WaitForB);<br />
writeln('A: ThreadB.Counter='+IntToStr(Form1.ThreadB.Counter));<br />
end;<br />
end;<br />
<br />
{ TThreadB }<br />
<br />
procedure TThreadB.Execute;<br />
var<br />
i: Integer;<br />
begin<br />
Counter:=0;<br />
while not Application.Terminated do begin<br />
// B: Working ...<br />
Sleep(1500);<br />
inc(Counter);<br />
// wake A<br />
RtlEventSetEvent(Form1.ThreadA.WaitForB);<br />
end;<br />
end;</delphi><br />
<br />
Note: RtlEventSetEvent can be called before RtlEventWaitFor. Then RtlEventWaitFor will return immediately. Use RTLeventResetEvent to clear a flag.<br />
<br />
== Fork ==<br />
When forking in a multi-threaded application, be aware that any threads created and running BEFORE the fork (or fpFork) call, will NOT be running in the child process. As stated on the fork() man page, any threads that were running before the fork call, their state will be undefined.<br />
<br />
So be aware of any threads initializing before the call (including on the initialization section). They will NOT work.<br />
<br />
== Parallel procedures/loops ==<br />
A special case of multi threading is running a single procedure in parallel. See [[Parallel procedures]].<br />
<br />
== Distributed computing ==<br />
The next higher steps after multi threading is running the threads on multiple machines. <br />
* You can use one of the TCP suites like synapse, lnet or indy for communications. This gives you maximum flexibility and is mostly used for loosely connected Client / Server applications.<br />
* You can use message passing libraries like [[MPICH]], which are used for HPC (High Performance Computing) on clusters.<br />
<br />
<br />
<br />
== External threads ==<br />
To make Free Pascal's threading system to work properly, each newly created FPC thread needs to be initialized (more exactly, the exception, I/O system and threadvar system per thread needs to be initialized so threadvars and heap are working). That is fully automatically done for you if you use BeginThread (or indirectly by using the TThread class). However, if you use threads that were created without BeginThread (i.e. external threads), additional work (currently) might be required. External threads also include those that were created in external C libraries (.DLL/.so). <br />
<br />
<br />
Things to consider when using external threads (might not be needed in all or future compiler versions): <br />
<br />
* Do not use external threads at all - use FPC threads. If can you can get control over how the thread is created, create the thread by yourself by using BeginThread.<br />
<br />
If the calling convention doesn't fit (e.g. if your original thread function needs cdecl calling convention but BeginThread needs pascal convention, create a record, store the original required thread function in it, and call that function in your pascal thread function: <br />
<br />
<Delphi>type <br />
TCdeclThreadFunc = function (user_data:Pointer):Pointer;cdecl; <br />
<br />
PCdeclThreadFuncData = ^TCdeclThreadFunc; <br />
TCdeclThreadFuncData = record<br />
Func: TCdeclThreadFunc; //cdecl function<br />
Data: Pointer; //original data<br />
end; <br />
<br />
// The Pascal thread calls the cdecl function<br />
function C2P_Translator(FuncData: pointer) : ptrint;<br />
var<br />
ThreadData: TCdeclThreadFuncData;<br />
begin <br />
ThreadData := PCdeclThreadFuncData(FuncData)^;<br />
Result := ptrint(ThreadData.Func(ThreadData.Data));<br />
end; <br />
<br />
procedure CreatePascalThread;<br />
var<br />
ThreadData: PCdeclThreadFunc; <br />
begin <br />
New(ThreadData);<br />
// this is the desired cdecl thread function<br />
ThreadData^.Func := func; <br />
ThreadData^.Data := user_data; <br />
// this creates the Pascal thread<br />
BeginThread(@C2P_Translator, ThreadData );<br />
end;</Delphi><br />
<br />
<br />
* Initialize the FPC's threading system by creating a dummy thread. If you don't create any Pascal thread in your app, the thread system won't be initialized (and thus threadvars won't work and thus heap will not work correctly). <br />
<br />
<Delphi>type<br />
tc = class(tthread)<br />
procedure execute;override;<br />
end;<br />
<br />
procedure tc.execute;<br />
begin<br />
end;<br />
<br />
{ main program } <br />
begin<br />
{ initialise threading system }<br />
with tc.create(false) do<br />
begin<br />
waitfor;<br />
free;<br />
end;<br />
{ ... your code follows } <br />
end.</Delphi><br />
<br />
(After the threading system is initialized, the runtime may set the system variable "IsMultiThread" to true which is used by FPC routines to perform locks here and there. You should not set this variable manually.) <br />
<br />
<br />
* If for some reason this doesn't work for you, try this code in your external thread function:<br />
<br />
<Delphi>function ExternalThread(param: Pointer): LongInt; stdcall;<br />
var<br />
tm: TThreadManager;<br />
begin<br />
GetThreadManager(tm);<br />
tm.AllocateThreadVars;<br />
InitThread(1000000); // adjust inital stack size here<br />
<br />
{ do something threaded here ... }<br />
<br />
Result:=0;<br />
end;</Delphi><br />
<br />
<br />
=== Identifying external threads ===<br />
Sometimes you even don't know if you have to deal with external threads (e.g. if some C library makes a callback). This can help to analyse this: <br />
<br />
1. Ask the OS for the ID of the current thread at your application's start <br />
<br />
<Delphi>Win32: GetCurrentThreadID();<br />
Darwin: GetThreadID(); <br />
Linux: TThreadID(pthread_self);</Delphi><br />
<br />
2. Ask again for the ID of the current thread inside the thread function and compare this by the result of step 1.<br />
<br />
==See also==<br />
<br />
* [[Streaming components]]<br />
* [[Manager Worker Threads System]]<br />
* [[Array of threads]]<br />
[[Category:Tutorials]]</div>Mvampire