Example of multi-threaded application: array of threads/es

From Free Pascal wiki
Jump to: navigation, search

English (en) español (es) 日本語 (ja)

   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:

 var resultados: integer;
 Archivo: text;
 begin
 ...
 for i:=1 to n do begin
  MiProcedimiento(resultado);
  Writeln(Archivo,resultado);
 end;
 ...

   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:

 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.

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).

 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.

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).

 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;