Newsgroup: it.comp.lang.delphi
Titolo: Thread e Notifiche

Buongiorno,
dato un semplice thread ho la necessità di monitorarne il progresso
tramite un sistema di logging che sia il più possibile flessibile.
Vorrei cioè catturare gli eventi generati all'interno della Execute e
visualizzarli in un qualche tipo di controllo nella form chiamante.
Di seguito un esempio di codice che sembra funzionare. Ma è sicuro?
Dove andrebbe gestita la Syncronize?
Grazie,
--------------------------------------
unit uFrm_Test;
interface
uses
  Classes, Forms, stdctrls, Controls, windows, sysutils;
type
  TFrm_Test = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
  private
  public
    procedure DoLogEvent (ASender: TObject; AMsg : string);
  end;
  TLogEvent =  procedure (Sender: TObject; sMsg : string) of object;
  MyThread = class(TThread)
  private
    procedure DoLog(smessage: String);
  protected
    FOnLogEvent : TLogEvent;
    procedure Execute; override;
  public
    Title : String;
    constructor Create(ATitle : String; ALogEvent : TLogEvent);
  end;
var
  Frm_Test : TFrm_Test;
  iNumThreads : Integer = 0;
implementation
{$R *.dfm}
constructor MyThread.Create(ATitle : String; ALogEvent : TLogEvent);
begin
  // Assign log event
  FOnLogEvent := ALogEvent;
  FreeOnterminate := TRUE;
  Title := ATitle;
  inherited Create(FALSE);
end;
// Wrapper for thread logging event
procedure MyThread.DoLog(smessage : String);
Begin
  if Assigned(FOnLogEvent) then
    FOnLogEvent (Self, sMessage); // Calls "outer" procedure
End;
// Execute
procedure MyThread.Execute;
begin
  // Step1
  DoLog('Step 1: started');
  Sleep(10000); // Simulate code execution ....
  DoLog('Step 1: finished');
    // Step2
  DoLog('Step 2: started');
  Sleep(5000); // Simulate code execution....
  DoLog('Step 2: finished');
end;
// Procedure for handling thread log notification
procedure TFrm_Test.Button1Click(Sender: TObject);
begin
  MyThread.Create(edit1.text, DoLogEvent);
  inc(iNumThreads);
  edit1.text := 'T' + IntToStr(iNumThreads);
end;
procedure TFrm_Test.DoLogEvent(ASender: TObject; AMsg: string);
var
  s : String;
begin
  if (ASender is MyThread) then
  Begin
    s := format('[%s : %s] %s', [datetimetostr(now), MyThread
(ASender).Title, AMsg]);
    memo1.Lines.Add(s);
  End;
end;
end.

Oliver Fasoli ha scritto:
<snip>
L'unico modo per consentire una comunicazione "sicura" tra thread
secondari e thread principale � quello di usare i messaggi. Fai in modo
che il tuo thread secondario mandi dei "custom message" al thread
principale in ogni fase di lavoro che vuol segnalare. Vedrai che tutto
funzioner� a dovere, senza pericoli di deadlock. Evita quando possibile le
sincronizzazioni, le elaborazioni asincrone sono sempre le pi� sicure.
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

 Dove andrebbe gestita la Syncronize?
In uno scenario molto simile al tuo, io ho utilizzato una 
tcricalsession (CS) in questo modo.
Il thread padre costruisce la CS e la passa ai thread figli nella 
create.
I thread figli memorizzano in una loro variabile il puntatore alla CS 
e, nel momento di accedere alla risorsa condivisa dal padre, fanno un 
aquire sulla CS, eseguono il codice, e rilasciano la CS.
    try
      FCriticalSection.Acquire;
      (codice di business logic)
    finally
    	FCriticalSection.Release;
    end;
in questo modo il tutto mi gira bene, senza rallentamenti nï¿œ deadlock, 
(il che non significa nulla ovviamente visto che cmq tutto questo 
dipende dalle race condition che si verificano)
Ad ogni modo, se vuoi evitare le race condition, segui l'approccio 
suggerito da Laforgia e passa ad un paradigma asincrono utilizzanto i 
messaggi di winzozz.
-- 
Morde

 messaggi di winzozz.
A volte non � neanche da escludere l'ipotesi multiprocesso. Sotto Windows
� un po' dispendioso per� a volte pu� essere molto comodo, sicuro e
scalabile.


 A volte non ï¿œ neanche da escludere l'ipotesi multiprocesso.
Questo ï¿œ interessante.
Io ho fatto uso al massimo di thread che generavano altri thread e cosï¿œ 
via, alcuni concorrenti altri indipendenti. Non ho mai dovuto ricorrere 
al multiprocesso.
Per multiprocesso credo che ti riferisci all'utilizzo di piï¿œ processi 
concorrenti e/o cooperanti tra loro, immagino per esempio alla 
creazione di piï¿œ "agenti" tramite createprocess.
In quale campo ti sei trovato ad utilizzare il multiprocesso? Con che 
architettura?
-- 
Morde

    end;
Secondo me, la chiamata al metodo Acquire() andrebbe fuori dal costrutto 
*try...finally*.
    FCriticalSection.Acquire;
    try
      // ...codice...
    finally
        FCriticalSection.Release;
    end;
Ciao,
   Marco.
-- 
MARCO BREVEGLIERI
  (
http://www.marco.breveglieri.name
)

 architettura?
E' un approccio che seguo quasi sempre, per esempio, quando ho una mole di
dati la cui elaborazione pu� essere suddivisa in task paralleli. Di solito
esiste un processo principale che coordina processi figli, i quali
comunicano col "padre" quando richiesto. A volte per esempio mi � capitato
di dover fare molte richieste HTTP ed elaborare le risposte. Dato che una
richiesta del genere � sincrona, usare un solo thread/processo ovviamente
� poco efficiente, quindi delego a una batteria di processi i quali
comunicano le risposte appena pronte o le salvano direttamente su DB. Il
vantaggio evidente � che ti concentri sulla costruzione del singolo task,
sapendo gi� che una parallellizzazione sicura la implementa il sistema
operativo. L'architettura migliore per svolgere questi compiti �
ovviamente Unix/Linux, perch� i processi hanno un certo peso sotto Windows
(la gestione stessa tramite fork � pi� efficiente), per� anche sotto
questa piattaforma non ho avuto grandi problemi, se il numero dei processi
richiesti non scala troppo (ma si sa che � bene sempre ragionare per
"pool" + coda di elaborazione, sia in caso di thread che di processi).


 esiste un processo principale che coordina processi figli
Aggiungo che questo meccanismo consente anche di scalare dinamicamente con
facilit�, nel senso che volendo posso lanciare manualmente un processo che
si registra presso il processo padre ed entra a far parte del pool. 


 si registra presso il processo padre ed entra a far parte del pool.
Questa parte ï¿œ interessante: per gestire la comunicazione, hai creato 
un protocollo basato sui messaggi oppure ti sei limitato ad uno scambio 
di messaggi piuttosto semplice tipo richiesta->consenti->registra?
ciao
-- 
Morde

 *try...finally*.
Grazie del consiglio, in effetti sarebbe piï¿œ corretto fare l'aquire 
fuori dal try..finally. :)
Ciao
-- 
Morde

 di messaggi piuttosto semplice tipo richiesta->consenti->registra?
E' una normale logica di publish/subscribe[1]. Un protocollo va comunque
creato. Io sotto Windows prediligo sempre lo scambio di messaggi. Il
protocollo consiste sostanzialmente nel decidere quali messaggi mandare ed
accettare e quali parametri con essi.
[1] 
http://en.wikipedia.org/wiki/Publish/subscribe


Grazie a tutti per i suggerimenti,
sto ora sperimentando le varie proposte.