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

From Lazarus wiki
Jump to navigationJump to search
Line 1: Line 1:
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.
+
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.
  
 
Let's assume that we have the following loop:
 
Let's assume that we have the following loop:
<delphi>
+
<delphi>var results: integer;
var results: integer;
 
 
     File: text;
 
     File: text;
 
begin
 
begin
Line 14: Line 13:
 
end;
 
end;
  
...
+
...</delphi>
</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.
 
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.
  
Line 24: Line 23:
  
 
I use a separate unit for global variables:
 
I use a separate unit for global variables:
<delphi>
+
<delphi>unit Global;
unit Global;
 
 
{$mode objfpc}{$H+}
 
{$mode objfpc}{$H+}
 
interface
 
interface
Line 33: Line 31:
  
 
implementation
 
implementation
end.
+
end.</delphi>
</delphi>
 
  
 
== 2. Add threads class. ==
 
== 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).
 
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;
+
<delphi>unit MyThreads;
  
 
{$mode objfpc}{$H+}
 
{$mode objfpc}{$H+}
Line 79: Line 76:
 
       end;
 
       end;
 
   end;
 
   end;
end.                                    
+
end.</delphi>
</delphi>
 
  
 
== 3. Rewrite main program. ==
 
== 3. Rewrite main program. ==
Line 87: Line 83:
  
 
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').
 
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,
+
<delphi>uses  cthreads,
 
//    cmem,
 
//    cmem,
 
//    pthreads,
 
//    pthreads,
Line 126: Line 122:
  
 
Terminate;
 
Terminate;
end;
+
end;</delphi>
</delphi>
+
 
 +
[[Category:Tutorials]]

Revision as of 05:46, 8 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 Multithreaded 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(results[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 number_of_threads*(n div number_of_threads) do Writeln(file,results[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>