Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Lazarus, FreePascal, Kylix, etc. (https://www.clubdelphi.com/foros/forumdisplay.php?f=14)
-   -   Información sobre hilos de ejecución (https://www.clubdelphi.com/foros/showthread.php?t=87263)

danielmj 04-12-2014 14:31:24

Información sobre hilos de ejecución
 
Hola, quiero mirarme como funcionan y como se crean los hilos de ejecución, he encontrado cosas en internet, pero no me aclaran mucho, ¿sabeis alguna explicación completa y digamos que didactica para comprender como funcionan y como se crean? La verdad es que tengo varios programas hechos por mi, que practicamente están terminados pero consumen muchos recursos y claro llega un momento en que se cuelgan, y quería retomarlos y meter los procesos mas pesados dentro de hilos. un saludo.

Ñuño Martínez 04-12-2014 15:37:20

No está mal que quieras aprender a usar hilos de ejecución, pero lo primero que debes tener en cuenta es que no siempre es mejor. Es decir, dices que tu programa se cuelga porque usa muchos recursos: hacer que funcione en otro hilo no tiene por qué solucionar el problema. A veces (casi siempre, en mi experiencia) la solución es otra (mejor uso de la memoria, por ejemplo).

Pero como digo, no está mal que quieras aprender a usar hilos porque son útiles en muchas ocasiones, así que ahí va.

Lo más simple es usar la clase TThread. Es bastante simple: hay que extender la clase TThread para añadirle las propiedades (datos) que necesite, e implementar el método "Execute", que es el código que se ejecutará en paralelo. Aquí tienes un ejemplo sencillito para iniciarte. Es algo viejo (de 2008), pero dudo que haya cambiado mucho la cosa. También tienes esta conversación en la que se habla del tema.

Espero que te ayude a empezar.

nlsgarcia 04-12-2014 15:40:44

danielmj,

Cita:

...Información sobre hilos de ejecución...¿sabéis alguna explicación completa y digamos que didáctica para comprender como funcionan y como se crean?...
:rolleyes:

Revisa este link:
Espero sea útil :)

Nelson.

danielmj 04-12-2014 16:36:09

buenas,

gracias voy a revisar a ver que tal se me da, lo que he leido hasta ahora se me escapa un poco pero bueno, lo he leido mas o menos por encima sin detenerme mucho.
Un saludo.

danielmj 04-12-2014 22:09:07

Hola,

Bueno ya he empezado a experimentar con hilos, he implementado un hilo a una aplicacion que ya tenia hecha y he conseguido que corra, pero en un momento dado, no siempre el mismo, me tira este error...


Sé que el error no se da en el mismo momento siempre, por una barra de progreso, a veces se rellena mas y otras menos, antes de tirar el error. También he agregado al programa la unidad SynchedThreads, que si no me equivoco (y si estoy en un error que alguien me corrija) es para sincronizar la vcl, creo.

El error me marca la linea 246, y esa linea tiene este codigo:
Código Delphi [-]
procedure TThread1.Execute;
var
begin
...
form1.button4.enabled:= true;
...
end;

Por cierto, como veis el mensaje de error sale en español, esto es por que es lazarus, sé que hay una seccion para lazarus en el foro, pero entre que empece el hilo sobre delphi y que el código es igual, me pareció acertado ponerlo aquí, si está mal que algún administrador lo mueva.

En fin ¿alguien sabe este error a que obedece?
Un saludo y gracias.

Ñuño Martínez 05-12-2014 08:58:25

Cita:

Empezado por danielmj (Mensaje 486107)
Hola,

Bueno ya he empezado a experimentar con hilos, he implementado un hilo a una aplicacion que ya tenia hecha y he conseguido que corra, pero en un momento dado, no siempre el mismo, me tira este error...

(...)

Pregunto: ¿Has hecho programas de prueba, más simples, antes?

Ten en cuenta que una aplicación que funcione con hilos necesita una planificación diferente que las aplicaciones que no los usan. Principalmente, en el acceso a memoria. Es posible que en algún punto uno de los hilos intente acceder a alguna variable u objeto de otro hilo diferente sin realizar la necesaria comprobación de sincronización, lo que puede ser la razón por la que salte el error en momentos deferentes. Lo más complicado es cuando hay ventanas/formularios implicados. Hay que estar seguro de en qué hilo se crea para asegurarse de hacer la sincronización adecuada de ser necesario.

danielmj 05-12-2014 09:25:27

Hola buenos dias,

Si, probé los hilos en un par de ejemplos mas fáciles o sencillos por ejemplo este y no fallaba. He leido por la web, que encontrar errores provocados en o por (no sé muy bien si culpar al hilo de ejecución), no es cosa sencilla, así que si bien, en esta aplicación mia en concreto, es necesario un hilo (muchos calculos sobre rangos muy altos), sobre todo por que la aplicación se cuelga y no se puede ni minimizar, si me saltan estos errores ¿donde demonios está la fuente de ese error? como dije, uso la unidad SynchedThreads, que muchos conoceran y que circula por la web, y la uso esperando precisamente la sincronización de la vcl, pero aún así me ha dado error. También es cierto, que es el primer contacto sobre hilos y una aplicación real (no ejemplos, pero si de uso personal), así que bueno, habrá que seguir investigando.
Si alguien está interesado en probar el hilo en mi programa, podría adjuntar el proyecto, eso si, tened en cuenta que está hecho en lazarus.

Neftali [Germán.Estévez] 05-12-2014 10:06:55

Cita:

Empezado por danielmj (Mensaje 486107)
Código Delphi [-]
procedure TThread1.Execute;
var
begin
...
form1.button4.enabled:= true;
...
end;

Bueno, viendo esa línea te diría que es normal el error.
Hay que tener claro que la VCL de por sí, no es "Thread-safe". Eso se traduce en que desde dentro de un hilo no puedes acceder "alegremente" a componentes y código que tengas fuera de él.

Es decir, desde dentro del hilo, NO PUEDES llamar "directamente" a un botón del formulario (Form1.Button4.Enabled).

Lo ideal es que los hilos se utilicen para relizar procesos que no inviolucren elementos visuales, como son formularios, componentes,...
Está claro que en algunos casos debemos modificarlos, por ejemplo, una barra de progreso para indicar el estado, labels, o si es el caso, el botón4 del form1.
Si esto es necesario, está contemplado y para ello está el método Synchronize.

Como te he dicho la VCL no es "thread-safe" por lo tanto tenemos que asegurarnos que cuendo accedemos desde el thread a "elementos externos" lo hagamos de forma exclusiva. Para eso sirve el Synchronize.

Aquí tienes algún ejemplo:

Y aquí en mi página un par más:
http://neftali.clubdelphi.com/?p=146
http://neftali.clubdelphi.com/?p=149

Me centro en estos ultimos para explicarte lo que más arriba apunto.

En el primero de los ejemplo, hay varios componentes del fiormulario que se deben modificar (similar a lo que haces tú con el edit).
Verás que en el thread NO SE LLAMA directamente a este código:

Código Delphi [-]
  // Incrementar un progressbar del formulario
  pb.Position := (pb.Position + FStep) MOD 100;

O a este otro:

Código Delphi [-]
  // Cambiar el color de un panel del formulario
  FColor := RGB(Random(255), Random(255), Random(255));
  pnb.Color := FColor;

Hay que colocarlos en un procedimiento y llamarlos con el método anterior; Así el código resultante es algo así:

Código Delphi [-]
procedure TPanelColorThread.ChangeColor;
begin
  FColor := RGB(Random(255), Random(255), Random(255));
  pnb.Color := FColor;
end;

...

  // La llamada
  while true do begin
    Synchronize(ChangeColor);
  end;

Y en el caso del progressbar, es algo similar.

Neftali [Germán.Estévez] 05-12-2014 10:10:34

En tu caso, tu código:
Código Delphi [-]
procedure TThread1.Execute;
var
begin
  ...
  form1.button4.enabled:= true;
  ...
end;

Debe quedar similas a este:
Código Delphi [-]
...

procedure TThread1.UpdateEdit();
var
begin
  ...
  form1.button4.enabled:= true;
  ...
end;


procedure TThread1.Execute;
var
begin
  ...
  Synchronize(UpdateEdit);
  ...
end;

Esto significa que ese código (UpdateEdit) se ejecutará de forma excluyente, para que no de problemas.

CONCLUSIÓN: Si utilizamos muchos Synchronize dentro de un thread es malo, porque continuamente estamos utilizando código "excluyente", por lo que la "gracia" del thread se pierde. De ahí que el Synchronize deba utilizarse para las cosas imprescindibles.

danielmj 05-12-2014 15:03:07

1 Archivos Adjunto(s)
Hola Neftali,

Aver mira tengo esta unidad donde se ejecuta el thread:
Código Delphi [-]
unit unit2;

interface

uses SysUtils, Classes;

type
  Thilo = class(tThread)
  private
    procedure updatEdit;

  protected
    procedure Execute; override;
  end;

implementation

uses unit1;

//Thilo

procedure tHilo.execute;
begin
  synchronize(updatEdit);
end;

procedure tHilo.updatEdit;
var
  i: integer;

begin
  form1.progressBar1.max:= 1000000;
  for i:= 0 to form1.progressBar1.max -1 do
    form1.progressBar1.position:= i;
end;
end.

Por otra parte, en la llamada del button1 tengo esto:
Código Delphi [-]
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls;

type
  TForm1 = class(TForm)
    ProgressBar1: TProgressBar;
    Button1: TButton;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation
uses unit2;

var
    FMiHilo: THilo;
{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  FMiHilo := THilo.Create(false);
end;

end.

Como ves el hilo lo hago en una unidad aparte de la del formulario, y siemplemente hago la llamada. El caso es que cuando se ejecuta, en un componente tEdit que he situado en el formulario para ver si podía escribir mientras trabajaba el bucle, sigo sin poder hacer nada. es decir que se ha "colgado" y no puedo hacer nada hasta que acaba. He intentado hacerlo como me indicabas, usando un procedimiento "independiente" sin controles visuales (salvo la barra de progreso). Entonces, ¿donde está mi error? ¿en que me estoy equivocando para que el edit quede bloqueado por el bucle?

Me he tomado la libertad de adjuntar el ejemplo que me he inventado digamos, para probar los hilos por si podeis echarle un vistazo y ver donde falla.

Un saludo.

Neftali [Germán.Estévez] 05-12-2014 15:45:40

Es normal que no puedas escribir nada.

Tal y como te he comentado antes, lo que pones a ejecutar en el synchronize, es código que no se ejecuta en paralelo.
Para que me entiendas es algo así. Cuando llamas al synchronize, el thread detiene el programa original, ejecuta lo que hay dentro del synchronize y luego vuelve a el control al programa principal.

Por eso he comentado que en el synchronize hay que poner el mínimo código posible.

Según la explicación anterior, tú has hecho justo TODO lo contrario. Has colocado TODO el código del hilo en el synchronize. Por lo tanto tu hilo, tal y como está, no saca ningún provecho a las ventajas de ejecutar código en paralelo.

Cambia tu código por este, y aprecia las diferencias:

Código Delphi [-]
//Thilo
procedure tHilo.execute;
var
  i:integer;
begin

  // para inicializar el Max
  synchronize(updatEdit);

  for i := 0 to form1.progressBar1.max -1 do begin
    cont := i;
    synchronize(updatEdit);
 
    // para que te de tiempo a hacer algo....
    sleep(10);
  end;
end;

procedure tHilo.updatEdit;
begin
  form1.progressBar1.max:= 1000;
  form1.progressBar1.position:= cont;
end;

danielmj 05-12-2014 17:39:17

Hola neftali, pues si que hay diferencia si, voy a seguir haciendo mas pruebas a ver si puedo sacarle partido a los hilos, la verdad es que... siendo filosofico (jaja) los hilos me resultan hirientemente útiles, lo de hirientes es por que o lo haces bien o a la menor oportunidad te darán la "patada"

Un saludo.

danielmj 08-12-2014 16:42:43

1 Archivos Adjunto(s)
Hola buenas de nuevo,

a ver segun entiendo, en un hilo no deben haber referencias a componentes visuales, pero en ese caso, si las operaciones de un procedimiento incluyen que se vaya mostrando información ¿como seria? ¿el hilo debería devolver el control a un procedimiento para "pintar" la informacion en una etiqueta (por ejemplo) y luego este procedimiento devolver el control al hilo para continuar? ¿eso sería así, pasandose el control de uno a otro hasta terminar? iba a poner un ejemplo real de la situación que tengo entre manos, pero son muchas lineas y no me deja, así que lo adjunto a este mensaje.

Tengo este procedimiento...
Código Delphi [-]
Procedure TPrincipal.Button1Click(Sender: TObject);
begin
  TrocearArchivo(labEdit1.Text,2048);
  button2.Enabled:= true;
end;
... que hace la llamada al procedimiento adjunto, ¿que parte del procedimiento debe estar fuera del hilo y cual dentro?

Un saludo y gracias.

Neftali [Germán.Estévez] 09-12-2014 12:54:49

Cita:

Empezado por danielmj (Mensaje 486281)
a ver segun entiendo, en un hilo no deben haber referencias a componentes visuales, pero en ese caso, si las operaciones de un procedimiento incluyen que se vaya mostrando información ¿como seria? ¿el hilo debería devolver el control a un procedimiento para "pintar" la informacion en una etiqueta (por ejemplo) y luego este procedimiento devolver el control al hilo para continuar? ¿eso sería así, pasandose el control de uno a otro hasta terminar? iba a poner un ejemplo real de la situación que tengo entre manos, pero son muchas lineas y no me deja, así que lo adjunto a este mensaje.

Tengo este procedimiento...
Código Delphi [-]
Procedure TPrincipal.Button1Click(Sender: TObject);
begin
  TrocearArchivo(labEdit1.Text,2048);
  button2.Enabled:= true;
end;
... que hace la llamada al procedimiento adjunto, ¿que parte del procedimiento debe estar fuera del hilo y cual dentro?

Un saludo y gracias.

Lo primero decir, que si no lees las cosas atentamente no llegaremos a ningún sitio, pues no nos vamos a entender...
Yo no he dicho que: "en un hilo no deben haber referencias a componentes visuales"

Mis palabras fueron las siguientes:

Eso se traduce en que desde dentro de un hilo no puedes acceder "alegremente" a componentes y código que tengas fuera de él..."
"Es decir, desde dentro del hilo, NO PUEDES llamar "directamente" a un botón del formulario (Form1.Button4.Enabled)...."
"...por lo tanto tenemos que asegurarnos que cuando accedemos desde el thread a "elementos externos" lo hagamos de forma exclusiva."

Creo que la diferencia es evidente.

SI se puede acceder a elementos externos como componentes, pero NO SE PUEDER HACER DIRECTAMENTE. Debes hacerlo utilizando Synchronize.

Neftali [Germán.Estévez] 09-12-2014 12:56:52

Cita:

Empezado por danielmj (Mensaje 486281)
pero en ese caso, si las operaciones de un procedimiento incluyen que se vaya mostrando información ¿como seria? ¿el hilo debería devolver el control a un procedimiento para "pintar" la informacion en una etiqueta (por ejemplo) y luego este procedimiento devolver el control al hilo para continuar? ¿eso sería así, pasandose el control de uno a otro hasta terminar?

SI.
Pero eso que parece tan complicado, se hace utilizando Synchonize de forma fácil.

Neftali [Germán.Estévez] 09-12-2014 13:06:49

Cita:

Empezado por danielmj (Mensaje 486281)
...así que lo adjunto a este mensaje.

Tengo este procedimiento...
Código Delphi [-]
Procedure TPrincipal.Button1Click(Sender: TObject);
begin
  TrocearArchivo(labEdit1.Text,2048);
  button2.Enabled:= true;
end;
... que hace la llamada al procedimiento adjunto, ¿que parte del procedimiento debe estar fuera del hilo y cual dentro?

Lo primero que hay que hacer es convertir ese "procedimiento", en un procedimiento "real"; Es decir, imagina que este procedimiento lo vas a llamar desde otro programa, donde no existen (porque se llaman diferente):
* principal.Memo1
* principal.LabEdit1
* principal.opc1.
* principal.Memo2
...

Estos elementos deberías estar como parámetros, porque en este procedimiento está "mezclando" lógica de negocio con temas visuales.

O te lo planteo de otra forma; Imagina que debes usar este procedimiento de TrocearArchivo llamándolo desde un programa que no tiene formularios.
¿Puedes hacerlo?

danielmj 09-12-2014 15:09:23

Hola neftalí,

a ver contesto desde mi ignorancia, a tu pregunta... que yo sepa se puede llamar a un programa desde otro, por lo que deduzco que si un programa x está formado unicamente por un procedimiento sin formularios (en este momento estoy pensando en una aplicacion de consola), otro programa podrá invocarlo o llamarlo sin problemas, así que mi respuesta es si.

Un saludo.

Ñuño Martínez 09-12-2014 15:22:49

El tema de los hilos es bastante complicado, ya que si dos hilos diferentes acceden al mismo tiempo al mismo dato puede haber problemas (por ejemplo que los dos intenten escribir a la vez, o que uno escriba y otro lea al mismo tiempo y por lo tanto los datos de quien lee estén corruptos por pillarlo a media escritura). Por ello hay que entender y usar los métodos y funciones de sincronización correctamente.

Respecto a la los componentes de la VCL, estos suelen ejecutarse en el llamado "hilo principal", por lo que otros hilos deben acceder a ellos de forma sincronizada.

Neftali [Germán.Estévez] 09-12-2014 17:33:13

Cita:

Empezado por danielmj (Mensaje 486340)
a ver contesto desde mi ignorancia, a tu pregunta... que yo sepa se puede llamar a un programa desde otro, por lo que deduzco que si un programa x está formado unicamente por un procedimiento sin formularios (en este momento estoy pensando en una aplicacion de consola), otro programa podrá invocarlo o llamarlo sin problemas, así que mi respuesta es si.

Me refería a tu procedimiento en concreto.
Crea un programa, con un formulario vacío y pon un botón que llame al procedimiento: TrocearArchivo(labEdit1.Text,2048)

¿Que pasará? Que falla en compilación porque el propio procedimiento hace referencia a controles visuales.
A eso me refería.
Esas referencias a objetos deberían estar como parámetros.


La franja horaria es GMT +2. Ahora son las 18:48:52.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi