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

From Lazarus wiki
Jump to navigationJump to search
(New page:    Aquí se muestra un ejemplo de cómo crear un montón de hilos y esperar a que terminen su trabajo (no se necesita ninguna sincronización). Estoy escribiendo esta tutor...)
 
Line 1: Line 1:
   Aquí se muestra un ejemplo de cómo crear un montón de hilos y esperar a que terminen su trabajo (no se  necesita ninguna sincronización). Estoy escribiendo esta tutoría, ya que no era evidente escribir este programa después de leer [[Multithreaded Application Tutorial/es|Tutoría de Aplicaciones Multihilo]] La aplicación está escrita para Linux x86_64.
+
   Aquí se muestra un ejemplo de cómo crear un montón de hilos y esperar a que terminen su trabajo (no se  necesita ninguna sincronización). Estoy escribiendo esta tutoría, ya que no era evidente escribir este programa después de leer [[Multithreaded Application Tutorial/es|Tutoría de Aplicaciones Multihilo]]. La aplicación está escrita para Linux x86_64.
  
 
   Supongamos que tenemos el siguiente bucle:
 
   Supongamos que tenemos el siguiente bucle:
<delphi>var resultados: integer;
+
<delphi> var resultados: integer;
 
  Archivo: text;
 
  Archivo: text;
 
  begin
 
  begin
Line 10: Line 10:
 
   Writeln(Archivo,resultado);
 
   Writeln(Archivo,resultado);
 
  end;
 
  end;
...</delphi>
+
...</delphi>
  
 
&nbsp;&nbsp;&nbsp;Este bucle se ejecuta u procedimiento que "calcula" algún "resultado". Después de esto, escribe esta matriz a un archivo de texto... Ahora queremos dividir este trabajo a muchos hilos en paralelo.
 
&nbsp;&nbsp;&nbsp;Este bucle se ejecuta u procedimiento que "calcula" algún "resultado". Después de esto, escribe esta matriz a un archivo de texto... Ahora queremos dividir este trabajo a muchos hilos en paralelo.
Line 16: Line 16:
 
&nbsp;&nbsp;&nbsp;Para hacer esto, necesitamos llevar a cabo los siguientes pasos:
 
&nbsp;&nbsp;&nbsp;Para hacer esto, necesitamos llevar a cabo los siguientes pasos:
  
== 1. Manage memory. ==  
+
== 1. Gestión de memoria ==  
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.
+
&nbsp;&nbsp;&nbsp;Tus hilos pueden leer las variables siempre que se necesite, pero no debería escribirse ningún dato en las estas variables (global), al mismo tiempo. Así que los hilos no pueden escribir nada en una variable como "resultado". Y no será capaz de escribir en Archivo. Por otra parte, estaba interesado en escribir los resultados en un archivo en orden (de 1 a n), y los hilos no lo harán. Así que el primer paso es crear la matriz global de los resultados (una celda por hilo). Si el procedimiento debe escribir algo a varias variables, se deben crear varias matrices o una matriz de registros.
  
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.
+
&nbsp;&nbsp;&nbsp;Usamos una unidad independiente para las variables globales:
 +
<delphi> unit Global;
 +
{$mode objfpc}{$H+}
 +
interface
 +
uses Classes, SysUtils;  
  
implementation
+
var resultados: array [1..100] of threads; //No vamos a crear más de 100 hilos.
end.</delphi>
 
  
== 2. Add threads class. ==
+
implementation
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;
+
end.</delphi>
  
{$mode objfpc}{$H+}
+
== 2. Añadimos la clase de los hilos ==
 +
&nbsp;&nbsp;&nbsp;También usamos una unidad separada para ella. Así hay que describir la propiedad ''Terminated'', como pública (para utilzarla desde el programa principal).
  
interface
+
<delphi> unit MisHilos;
  
uses
+
{$mode objfpc}{$H+}
 +
 
 +
interface
 +
 
 +
uses
 
   Classes, SysUtils;  
 
   Classes, SysUtils;  
  
 
  Type
 
  Type
     TMyThread = class(TThread)
+
     TMiHilo = class(TThread)
 
     private
 
     private
 
     protected
 
     protected
       procedure Execute; override;
+
       procedure Ejecutar; override;
 
     public
 
     public
       start,finish: integer;  //I will initialise these fields before running the thread. So each thread will receive a part of task.
+
       inicio,fin: integer;  //Se inician estos campos antes de ejecutar el hilo. Por lo que cada hilo recibirá una parte de la tarea.
       property Terminated;
+
       property Terminado;
       Constructor Create(CreateSuspended : boolean);
+
       Constructor Create(CrearSuspendido : boolean);
 
     end;
 
     end;
  
implementation
+
implementation
  
constructor TMyThread.Create(CreateSuspended : boolean);
+
constructor TMiHilo.Create(CrearSuspendido : boolean);
 
   begin
 
   begin
     FreeOnTerminate := True;
+
     LibeararAlTerminar := True;
     inherited Create(CreateSuspended);
+
     inherited Create(CrearSuspendido);
 
   end;
 
   end;
  
  procedure TMyThread.Execute;
+
procedure TMiHilo.Ejecutar;
 
   var
 
   var
 
     i: integer;
 
     i: integer;
 
   begin
 
   begin
   i:=start;
+
   i:=inicio;
     while (not Terminated) and (i<=finish) do
+
     while (not Terminado) and (i<=fin) do
 
       begin
 
       begin
           MyProcedure(results[i]);
+
           MiProcedimiento(resultados[i]);
 
           inc(i);
 
           inc(i);
           if i=finish+1 then Terminate;
+
           if i=fin+1 then Terminar;
 
       end;
 
       end;
 
   end;
 
   end;
end.</delphi>
+
end.</delphi>
  
== 3. Rewrite main program. ==
+
== 3. Reescribir el programa principal ==
  
You need to add 'cthreads' to the main unit, not to unit with threads!
+
&nbsp;&nbsp;&nbsp;Es necesario añadir "cthreads" a la unidad principal, ¡no a la unidad con los hilos!
  
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').
+
&nbsp;&nbsp;&nbsp;Consejos: mi IDE de Lazarus no fue capaz de depurar aplicaciones ''multihilo'', si no se usa "pthreads". He leído que si utiliza "cmem ', el programa funciona más rápido, pero recomiendo que para comprobar cada caso particular (mi programa se cuelga cuando uso" cmem).
  
<delphi>uses  cthreads,
+
<delphi> uses  cthreads,
//    cmem,
+
//    cmem,
//    pthreads,
+
//    pthreads,
 
       Classes, SysUtils, CustApp, MyThreads, Global
 
       Classes, SysUtils, CustApp, MyThreads, Global
  
var
+
var
ThreadArray: array [1..100] of TMyThread; //We will create no more than 100 threads.
+
  MatrizHilos: array [1..100] of TMiHilo; //No vamos a crear más de 100 hilos.
i, number_of_threads: integer;
+
  i, numero_de_hilos: integer;
begin
+
begin
  
number of threads:=100;
+
numero_de_hilos := 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.
+
while n div numero_de_hilos <1 do dec(numero_de_hilos); //Si n < 100 entonces no tenemos suficientes tareas para 100 hilos.
  
for i:=1 to number_of_threads do begin
+
for i:=1 to numero_de_hilos do begin
ThreadArray[i]:= TMyThread.Create(True);
+
  MatrizHilos[i]:= TMiHilo.Crear(True);
ThreadArray[i].start:=(i-1)*(n div number_of_threads)+1;;
+
  MatrizHilos[i].inicio:=(i-1)*(n div numero_de_hilos)+1;;
ThreadArray[i].finish:=i*(n div number_of_threads);
+
  MatrizHilos[i].fin:=i*(n div numero_de_hilos);
ThreadArray[i].Resume;
+
  MatrizHilos[i].Resumir;
end;
+
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.
+
for i:=1 to 10 do if not MatrizHilos[i].Terminado then Sleep(10); //Esperando a que los hilos terminen su trabajo.
                                                                                    //Or You can calculate a sum here and write only it.
 
  
//Now we should finish part of the task that couldn't be divided to 100 threads.
+
for i:=1 to numero_de_hilos * (n div numero_de_hilos) do Writeln(Archivo,resultados[i]); //Escribir los resultados en orden.
//For examle, if n=10050 then 10000 will be divided to 100 threads.  
+
                                                                                    //O Se puede calcular una suma aquí y escribirlo.
//And last 50 (< 100) we should finish in non-parallel mode.
+
//Ahora debemos terminar parte de la tarea que no puede ser dividido en 100 hilos.
//That's how I do this.  
+
//Por ejemplo, si n=10050 entonces 10000 se dividirá en 100 hilos.  
//Instead, You can write something like: 'if i=number_of_threads then ThreadArray[i].finish:=n;' in the loop above.
+
//Y los últimos 50 (< 100) que debe terminar en el modo no paralelo.
//In this case last thread will finish the job.
+
//Así lo hago.  
 +
//En su lugar, se puede escribir algo como: "si i = numero_de_hilos entonces MatrizHilos[i].fin: = n;. 'En el ciclo anterior.
 +
//En este casoel  último hilo debe terminar el trabajo.
  
if n mod number_of_threads<>0 then
+
if n numero_de_hilos <> 0 then
for i:=(n div number_of_threads) + 1 to n do begin
+
for i:=(n div numero_de_hilos) + 1 to n do begin
                                            MyProcedure(result);
+
                                              MiProcedimiento(resultado);
                                            Writeln(File,result);
+
                                              Writeln(Archivo,resultado);
                                            end;
+
                                              end;
  
Terminate;
+
Terminar;
end;</delphi>
+
end;</delphi>
  
 
[[Category:Castellano]][[Category:Español]]
 
[[Category:Castellano]][[Category:Español]]
 
[[Category:Tutorials/es]]
 
[[Category:Tutorials/es]]
 
[[Category:Parallel programming]]
 
[[Category:Parallel programming]]

Revision as of 22:46, 21 September 2011

   Aquí se muestra un ejemplo de cómo crear un montón de hilos y esperar a que terminen su trabajo (no se necesita ninguna sincronización). Estoy escribiendo esta tutoría, ya que no era evidente escribir este programa después de leer Tutoría de Aplicaciones Multihilo. La aplicación está escrita para Linux x86_64.

   Supongamos que tenemos el siguiente bucle: <delphi> var resultados: integer;

Archivo: text;
begin
...
for i:=1 to n do begin
 MiProcedimiento(resultado);
 Writeln(Archivo,resultado);
end;
...</delphi>

   Este bucle se ejecuta u procedimiento que "calcula" algún "resultado". Después de esto, escribe esta matriz a un archivo de texto... Ahora queremos dividir este trabajo a muchos hilos en paralelo.

   Para hacer esto, necesitamos llevar a cabo los siguientes pasos:

1. Gestión de memoria

   Tus hilos pueden leer las variables siempre que se necesite, pero no debería escribirse ningún dato en las estas variables (global), al mismo tiempo. Así que los hilos no pueden escribir nada en una variable como "resultado". Y no será capaz de escribir en Archivo. Por otra parte, estaba interesado en escribir los resultados en un archivo en orden (de 1 a n), y los hilos no lo harán. Así que el primer paso es crear la matriz global de los resultados (una celda por hilo). Si el procedimiento debe escribir algo a varias variables, se deben crear varias matrices o una matriz de registros.


   Usamos una unidad independiente para las variables globales: <delphi> unit Global;

{$mode objfpc}{$H+}
interface
uses Classes, SysUtils; 
var resultados: array [1..100] of threads; //No vamos a crear más de 100 hilos.
implementation
end.</delphi>

2. Añadimos la clase de los hilos

   También usamos una unidad separada para ella. Así hay que describir la propiedad Terminated, como pública (para utilzarla desde el programa principal).

<delphi> unit MisHilos;

{$mode objfpc}{$H+}
interface
uses
 Classes, SysUtils; 
Type
   TMiHilo = class(TThread)
   private
   protected
     procedure Ejecutar; override;
   public
     inicio,fin: integer;  //Se inician estos campos antes de ejecutar el hilo. Por lo que cada hilo recibirá una parte de la tarea.
     property Terminado;
     Constructor Create(CrearSuspendido : boolean);
   end;
implementation
constructor TMiHilo.Create(CrearSuspendido : boolean);
 begin
   LibeararAlTerminar := True;
   inherited Create(CrearSuspendido);
 end;
procedure TMiHilo.Ejecutar;
 var
   i: integer;
 begin
  i:=inicio;
   while (not Terminado) and (i<=fin) do
     begin
          MiProcedimiento(resultados[i]);
          inc(i);
          if i=fin+1 then Terminar;
     end;
 end;
end.</delphi>

3. Reescribir el programa principal

   Es necesario añadir "cthreads" a la unidad principal, ¡no a la unidad con los hilos!

   Consejos: mi IDE de Lazarus no fue capaz de depurar aplicaciones multihilo, si no se usa "pthreads". He leído que si utiliza "cmem ', el programa funciona más rápido, pero recomiendo que para comprobar cada caso particular (mi programa se cuelga cuando uso" cmem).

<delphi> uses cthreads,

//    cmem,
//    pthreads,
     Classes, SysUtils, CustApp, MyThreads, Global
var
 MatrizHilos: array [1..100] of TMiHilo; //No vamos a crear más de 100 hilos.
 i, numero_de_hilos: integer;
begin
numero_de_hilos := 100;
while n div numero_de_hilos <1 do dec(numero_de_hilos); //Si n < 100 entonces no tenemos suficientes tareas para 100 hilos.
for i:=1 to numero_de_hilos do begin
 MatrizHilos[i]:= TMiHilo.Crear(True);
 MatrizHilos[i].inicio:=(i-1)*(n div numero_de_hilos)+1;;
 MatrizHilos[i].fin:=i*(n div numero_de_hilos);
 MatrizHilos[i].Resumir;
end;
for i:=1 to 10 do if not MatrizHilos[i].Terminado then Sleep(10); //Esperando a que los hilos terminen su trabajo.
for i:=1 to numero_de_hilos * (n div numero_de_hilos) do Writeln(Archivo,resultados[i]); //Escribir los resultados en orden.
                                                                                    //O Se puede calcular una suma aquí y escribirlo.
//Ahora debemos terminar parte de la tarea que no puede ser dividido en 100 hilos.
//Por ejemplo, si n=10050 entonces 10000 se dividirá en 100 hilos. 
//Y los últimos 50 (< 100) que debe terminar en el modo no paralelo.
//Así lo hago. 
//En su lugar, se puede escribir algo como: "si i = numero_de_hilos entonces MatrizHilos[i].fin: = n;. 'En el ciclo anterior.
//En este casoel  último hilo debe terminar el trabajo.
if n numero_de_hilos <> 0 then
for i:=(n div numero_de_hilos) + 1 to n do begin
                                              MiProcedimiento(resultado);
                                              Writeln(Archivo,resultado);
                                             end;
Terminar;
end;</delphi>