Multi thread delphi

I have problems with multiple threads in delphi. I have a list of names (about 2,000 names), and I need to get some data for each name on my site. My system works fine except for flow control.

I want to create 10 threads, and when some threads finish, create more ... to the end of the list.

var Form1: TForm; tCount: Integer; //threads count implementation type TCheck = class(TThread) public constructor Create(Name: string); destructor Destroy; Override; protected procedure Execute; Override; end; MainT = class(TThread) protected procedure Execute; Override; end; destructor TCheck.Destroy; begin Dec(tCount); end; procedure MainT.Execute; var i: Integer; Load: TStringList; begin Load:=TStringList.Create; Load.LoadFromFile('C:\mynames.txt'); for i:= 0 to Load.Count -1 do begin if tCount = 10 then //if we have 10 threads running... begin repeat Sleep(1); until tCount < 10; end; TCheck.Create(Load.Strings[i]); TCheck.Start; Inc(tCount); end; end; // end of procedure 

Well, I did not set TCheck.Constructor, because the problem is how I check the number of threads created. I mean, my software just stops, without an error message, sometimes it checks 500 names, sometimes 150 names ...

Sorry for the bad english.

+6
source share
2 answers

Below is a solution for a streaming queue using generics.

Determine how many user threads you want, the depth of the queue, and simply run the DoSomeJob procedure from the stream.

Determine that your work with the string as a general procedure (in CaptureJob ).

When the queue is empty, consumer flows will be destroyed. The DoSomeJob procedure waits for all tasks to complete. You can easily turn this into a shared work pool by reusing threads without destroying them. The general structure of tasks also makes them suitable for processing various types of work.

Note that this queue runs on XE2 and higher. If you are on an old version of delphi, find a similar thread safety queue as suggested in the comments.

 uses Classes,SyncObjs,Generics.Collections; Type TMyConsumerItem = class(TThread) private  FQueue : TThreadedQueue<TProc>;  FSignal : TCountDownEvent; protected  procedure Execute; override; public  constructor Create( aQueue : TThreadedQueue<TProc>; aSignal : TCountdownEvent); end; constructor TMyConsumerItem.Create(aQueue: TThreadedQueue<TProc>); begin Inherited Create(false); Self.FreeOnTerminate := true; FQueue := aQueue; FSignal := aSignal; end; procedure TMyConsumerItem.Execute; var aProc : TProc; begin try repeat FQueue.PopItem(aProc); if not Assigned(aProc) then break; // Drop this thread aProc(); until Terminated; finally FSignal.Signal; end; end; procedure DoSomeJob(myListItems : TStringList); const cThreadCount = 10; cMyQueueDepth = 100; var i : Integer; aQueue : TThreadedQueue<TProc>; aCounter : TCountDownEvent; function CaptureJob( const aString : string) : TProc; begin Result := procedure begin // Do some job with aString end; end; begin aQueue := TThreadedQueue<TProc>.Create(cMyQueueDepth); aCounter := TCountDownEvent.Create(cThreadCount); try for i := 1 to cThreadCount do TMyConsumerItem.Create(aQueue,aCounter); for i := 0 to myListItems.Count-1 do begin aQueue.PushItem( CaptureJob( myListItems[i])); end; finally for i := 1 to cThreadCount do aQueue.PushItem(nil); aCounter.WaitFor; // Wait for threads to finish aCounter.Free; aQueue.Free; end; end; 

NB : Ken explains why your initialization and the start of threads are wrong. This proposal shows a better framework for solving such problems in a more general way.

+3
source

Unless you declare a variable to hold the return value of TCheck.Create , you cannot access TCheck.Start (there is no instance of TCheck that you can use to access the Start method).

The correct way would be to declare var Check: TCheck; inside MainT.Execute , and then save the return value:

 Check := TCheck.Create(Load[i]); { See note below } Check.Start; Inc(tCount); 

NOTE The default property of TStringList is Strings , so you do not need to use it. You can simply access Strings directly, as I said above. The following two lines are the same thing (but obviously one of them is shorter and easier to type):

 Load.Strings[i]; Load[i]; 

If you do not want to keep a link to TCheck , just change your code as a with block (including begin..end and not containing other code in the block (this is only the way I recommend using with ):

 with TCheck.Create(Load[i]) do begin Start; Inc(tCount); end; 

With that said, there are much better ways to do this, instead of creating / destroying all kinds of threads. As others have said, you can have a list of 10 threads and a work queue for them, so that everyone will process an element from Load , and then return to get another element to process when this is done, and repeat until the list completed, It's hard to say exactly how you do it, because it will depend on your version of Delphi. (Libraries are available that will do most of the work for you, such as OMNIThreadLibrary , but it is not available for some older versions of Delphi. Recent versions of Delphi also support TQueue and TObjectQueue , as well as some other types and functions that can be very useful.

(If you have another question about how to do this in a queue with a limited number of threads, this should be a new question , not what you add to this.)

+1
source

All Articles