Example of multi-threaded application: array of threads/es
│
English (en) │
español (es) │
日本語 (ja) │
polski (pl) │
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;