Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Threads en unit (https://www.clubdelphi.com/foros/showthread.php?t=90130)

Ramsay 12-04-2016 20:48:35

Threads en unit
 
Hola , estoy haciendo una unit que hace multithreading , el codigo de la unit :

Código Delphi [-]
unit probando;

{$POINTERMATH ON}

interface

uses
  System.SysUtils, Vcl.Dialogs, Windows, TlHelp32, ShellApi,
  Classes, Math, Vcl.Forms, Vcl.Imaging.jpeg, Vcl.StdCtrls, Vcl.Graphics,
  Vcl.Controls,
  ComObj, ActiveX, Variants, Messages;

type
  T_probando = class
  private

  public
    constructor Create;
    destructor Destroy; override;
    function loader_tester(argument: string; count: integer): string;
  end;

implementation

constructor T_probando.Create;
begin
  inherited Create;
  //
end;

destructor T_probando.Destroy;
begin
  inherited Destroy;
end;

// Function loader_tester

type
  thread_loader_tester = class(TThread)
  protected
    procedure Execute; override;
  public
    argument: string;
  end;

procedure thread_loader_tester.Execute;
var
  command: string;
  info: string;
begin
  TThread.Synchronize(nil,
    procedure
    begin
      command := 'c:/xampp/titulo.txt';
      info := '';
      ShellExecute(0, 'open', Pchar(command), Pchar(info), nil, SW_NORMAL);
    end);
end;

function T_probando.loader_tester(argument: string; count: integer): string;
var
  thread_now: thread_loader_tester;
  i: integer;
  fThreadRefCount: integer;
  check: integer;
begin
  try
    fThreadRefCount := 0;
    begin

      For i := 1 to count do
      begin
        thread_now := thread_loader_tester.Create(True);
        thread_now.argument := argument;
        thread_now.FreeOnTerminate := True;
        thread_now.Start;
        Inc(fThreadRefCount);
      end;

      While fThreadRefCount > 1 do
      // While (fThreadRefCount > 1) and (fThreadRefCount < count) do
      begin
        Application.ProcessMessages;
        CheckSynchronize;
      end;

      Result := 'ok';
    end;
    Result := 'ok';
  except
    begin
      Result := 'error';
    end;
  end;
end;

end.

Debe funcionar tanto en aplicaciones graficas como en consola , lamentablemente estoy mejorandola para que funcione en consolas , el codigo de la consola.

Código Delphi [-]
program test;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  Windows, System.SysUtils, probando;

var
  test_now: T_probando;

begin
  try
    test_now := T_probando.Create();
    Writeln('[+] Response');
    Writeln('response : '+test_now.loader_tester('c:/xampp/titulo.txt', 5));
    Readln;
  except
    on E: Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;

end.

Funciona , pero entra en un ciclo infinito :

Código Delphi [-]
While fThreadRefCount > 1 do

Intente checkeando que verificara la cantidad de threads y ahi terminara :

Código Delphi [-]
While (fThreadRefCount > 1) and (fThreadRefCount < count) do

Pero si hago esto no se ejecuta ningun thread.

:eek::eek::eek:

¿ Alguien podria ayudarme ?

jhonny 12-04-2016 21:19:13

Pensaría que quizá necesitas hacer un decremento de fThreadRefCount en el ciclo:


Código Delphi [-]
     While fThreadRefCount > 1 do
      begin
        Application.ProcessMessages;
        CheckSynchronize;
        Dec(fThreadRefCount); //Algo así quizá...
      end;

AgustinOrtu 12-04-2016 21:30:08

No entiendo nada de tu codigo

Por que usas un Syncronize para ejecutar un ShellExecute? Eso ya esta creando un proceso externo a tu aplicacion, no hace falta que sincronices nada

Por que el Application.ProcessMessages?
Por que el CheckSynchronize?

jhonny 12-04-2016 21:32:45

Cita:

Empezado por AgustinOrtu (Mensaje 504331)
No entiendo nada de tu codigo

Por que usas un Syncronize para ejecutar un ShellExecute? Eso ya esta creando un proceso externo a tu aplicacion, no hace falta que sincronices nada

Por que el Application.ProcessMessages?
Por que el CheckSynchronize?

Bueno, me hago las mismas preguntas, aunque asumí que son "para algo" y ya, por eso mi respuesta.

AgustinOrtu 12-04-2016 21:33:12

Para aplicaciones de consola, si se debe usar CheckSynchronize

Lee la documentacion

Es necesario asignar el valor del metodo a WakeMainThread El problema es que desde una aplicacion con interfaz grafica no deberias tocar esa variable, y usar TThread.Syncronize

Yo me replantearia el diseño

Ramsay 13-04-2016 00:01:39

El shellexecute es solo un ejemplo , en esa parte del codigo va todo el funcionamiento o el codigo que agregare en el futuro.Estoy estudiando multithreads pero hay muy pocos ejemplos.

Ramsay 13-04-2016 01:44:45

Perdon , hago otro post porque me olvide de un detalle importante , uso Synchronize porque estoy usandolo en una consola.

AgustinOrtu 13-04-2016 02:28:35

ShellExecute es asincronico, no te sirve para aprender threads

Mejor pon un for de i hasta algun numero muy grande, por ejemplo algun int64

Ramsay 13-04-2016 02:56:25

Ok , pero el tema principal como lo soluciono ? , intente como dijo jhonny pero aun asi vuelve a lo mismo , o se vuelve eterno o no se ejecuta ninguno.

AgustinOrtu 13-04-2016 03:55:17

Ok esto es muy interesante, admito como siempre que multithreading es dificil y me cuesta dominarlo. Pero encima en consola es distinto porque se te cierra el programa :D

Esto es lo que invente, es bastante "versatil"

Clase que hereda de TThread:

Código Delphi [-]
unit Unit4;

interface

uses
  Classes;

type
  TThreadInutil = class(TThread)
  private
    FName: string;
    FTope: Int64;
    FUltimoValor: Int64;
  protected
    procedure Execute; override;
  public
    constructor Create(CreateSuspended: Boolean; const AName: string; const ATope: Int64);
    property Name: string read FName;
    property Tope: Int64 read FTope;
    property UltimoValor: Int64 read FUltimoValor;
  end;

implementation

uses
  SysUtils;

constructor TThreadInutil.Create(CreateSuspended: Boolean; const AName: string; const ATope: Int64);
begin
  inherited Create(CreateSuspended);
  FreeOnTerminate := True;
  FName := AName;
  FUltimoValor := 0;
  if FTope > MaxLongint then
    FTope := MaxLongint
  else
    FTope := ATope;
end;

procedure TThreadInutil.Execute;
var
  I: Int64;
begin
  I := 0;
  while (I < Tope) and not Terminated do
  begin
    Inc(I);
    FUltimoValor := I;
    Synchronize(NIL, procedure begin WriteLn(Format('Name: %s I = %d', [Name, I])); end);
  end;
end;

end.

Basicamente cuenta desde 0 hasta el numero que le decimos en el constructor

En el metodo Execute que es reimplementado, imprime el "nombre" y el numero actual en la salida estandar, usando Syncronize

El programa principal crea dos TThreadInutiles, los cuales inician apenas termina su correspondiente constructor.

Luego, hay una pequeña clase TInformador porque el evento OnTerminate es de tipo TNotifyEvent y para agregarle un manejador hay que tener un objeto (TNotifyEvent es un procedure of object, no podes poner un procedimiento suelto, a menos no de forma idiomatica)

El informador recibe la notificacion cuando cada hilo termina su trabajo, y si es el "t1", le agrego una lectura de entrada estandar pidiendo un numero: si es 0 le pedimos al t2 que termine, sino que continue


Hay que consultar el estado de los Thread, si estan trabajando (propiedad Booleana Finished) y si alguno esta haciendo algo, es necesario invocar a CheckSyncronize

Código Delphi [-]
program Project2;
{$APPTYPE CONSOLE}

uses
  SysUtils,
  Classes,
  Unit4 in 'Unit4.pas';

type
  TInformador = class
  public
    procedure ThreadTerminado(Sender: TObject);
  end;

var
  t1, t2: TThreadInutil;
  Informador: TInformador;

procedure TInformador.ThreadTerminado(Sender: TObject);
var
  Num: Integer;
begin
  Writeln(Format('%s Termino: Ultimo Valor %d', [Sender.ClassName, TThreadInutil(Sender).UltimoValor]));

  if Sender = t1 then
  begin
    Writeln('Ingrese 0 para detener t2, cualquier numero para seguir..');
    Readln(Num);
    if Num = 0 then
      t2.Terminate;
  end;
end;

begin
  Informador := TInformador.Create;
  t1 := TThreadInutil.Create(False, 'Thread 1', 1500);
  t2 := TThreadInutil.Create(False, 'Thread 2', 1900);
  try
    t1.OnTerminate := Informador.ThreadTerminado;
    t2.OnTerminate := Informador.ThreadTerminado;
    while not(t1.Finished and t2.Finished) do
      CheckSynchronize;
    try
    except
      on E: Exception do
        Writeln(E.ClassName, ': ', E.Message);
    end;
  finally
    Informador.Free;
    Writeln;
    Writeln('Terminado. Presione ENTER para salir');
    Readln;
  end;

end.

Ramsay 13-04-2016 15:27:52

Te agradezco la ayuda AgustinOrtu , encontre una solucion poco elegante para mi codigo , uso una variable global que incremento en el TThread.Synchronize , entonces en el odioso bucle while :mad: , pongo una condicion donde si la variable global es igual al count hace un break y listo.Ahora me fijo como le puedo agregar un for a tu codigo para hacer multithreading.
gracias por la ayuda.

AgustinOrtu 13-04-2016 18:09:03

Yo sigo pensando que esta mal planteado tu codigo

En el ejemplo cree dos threads, queres crear mas con un for, entonces create una estructura de threads (lista, pila, cola, arreglo, lo que quieras) y los vas metiendo ahi

No es correcto usar Application.ProcessMessages en una aplicacion de consola, eso se usa desde una aplicacion VCL

El codigo no es seguro y cuando falle, la razon va a estar enterrada muy dentro y muy dificil de ver

Una variable global es un problema cuando usas thread por que el acceso tiene que ser sincronizado, no solamente la vas a usar para lectura sino que tambien la queres modificar

DopeRider 14-04-2016 15:59:31

Cita:

Empezado por Ramsay (Mensaje 504328)
Hola , estoy haciendo una unit que hace multithreading , el codigo de la unit : (...) ¿ Alguien podria ayudarme ?

¿Qué problema estás tratando de solucionar?


La franja horaria es GMT +2. Ahora son las 13:08:07.

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