Difference between revisions of "Example of multi-threaded application: array of threads"

From Free Pascal wiki
Jump to navigationJump to search
Line 30: Line 30:
 
uses Classes, SysUtils;  
 
uses Classes, SysUtils;  
  
var result: array [1..100] of threads; //We will create no more than 100 threads.
+
var results: array [1..100] of threads; //We will create no more than 100 threads.
  
 
implementation
 
implementation

Revision as of 22:30, 7 September 2010

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.

Let's assume that we have the following loop: <delphi> var results: integer;

   File: text;

begin

...

for i:=1 to n do begin MyProcedure(result); Writeln(File,result); end;

... </delphi> 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.

To do this, You need to perform the following steps:

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.

I use a separate unit for global variables: <delphi> unit Global; {$mode objfpc}{$H+} interface uses Classes, SysUtils;

var results: array [1..100] of threads; //We will create no more than 100 threads.

implementation end. </delphi>

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). <delphi> unit MyThreads;

{$mode objfpc}{$H+}

interface

uses

 Classes, SysUtils; 
Type
   TMyThread = class(TThread)
   private
   protected
     procedure Execute; override;
   public
     start,finish: integer;  //I will initialise these fields before running the thread. So each thread will receive a part of task.
     property Terminated;
     Constructor Create(CreateSuspended : boolean);
   end;

implementation

constructor TMyThread.Create(CreateSuspended : boolean);

 begin
   FreeOnTerminate := True;
   inherited Create(CreateSuspended);
 end;
 procedure TMyThread.Execute;
 var
   i: integer;
 begin
  i:=start;
   while (not Terminated) and (i<=finish) do
     begin
          MyProcedure(result[i]);
          inc(i);
          if i=finish+1 then Terminate;
     end;
 end;

end. </delphi>

3. Rewrite main program.

You need to add 'cthreads' to the main unit, not to unit with threads!

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'). <delphi> uses cthreads, // cmem, // pthreads,

     Classes, SysUtils, CustApp, MyThreads, Global

var

ThreadArray: array [1..100] of TMyThread; //We will create no more than 100 threads.
i, number_of_threads: integer;

begin

number of threads:=100;

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.

for i:=1 to number_of_threads do begin ThreadArray[i]:= TMyThread.Create(True); ThreadArray[i].start:=(i-1)*(n div number_of_threads)+1;; ThreadArray[i].finish:=i*(n div number_of_threads); ThreadArray[i].Resume; end;

for i:=1 to 10 do if not ThreadArray[i].Terminated then Sleep(10); //Waiting while threads will not finish their job.

for i:=1 to n do Writeln(file,result[i]); //Writing results in order.

                                         //Or You can calculate a sum here and write only it.

//Now we should finish part of the tast that couldn't be divided to 100 threads. //For examle, if n=10050 then 10000 will be divided to 100 threads. And last 50 (< 100) we should finish in non-parallel mode //That's how I do this. Instead, You can write something like: 'if i=number_of_threads then ThreadArray[i].finish:=n;' //In this case last thread will finish the job.

if n mod number_of_threads<>0 then for i:=(n div number_of_threads) + 1 to n do begin

                                            MyProcedure(result);
                                            Writeln(File,result);
                                            end;

Terminate; end; </delphi>