2009-12-02 16 views

risposta

4

Si consiglia inoltre di eseguire la procedura su un thread. Utilizzare quindi l'evento OnTerminate per ottenere il risultato. Sì, in questi giorni di .NET e C# siamo in qualche modo rovinati dalla forma facile e conveniente di eseguire i metodi in modo asincrono, ma questo è il modo in cui funziona su Delphi.

+0

Ho una versione di quello che sto facendo in C# jeje, ora che sto cercando di tradurre in delphi (perché non voglio usare .NET) vedo quello che stai dicendo. Grazie. – Sebastian

+0

Finalmente ho funzionato solo con un semplice BeginThread, non mi interessa veramente il risultato della procedura, deve solo fare qualcosa o uscire. Grazie per il tuo messaggio. – Sebastian

16

Se si sta chiedendo se il VCL ha qualcosa di like BeginInvoke in .NET pronto per l'uso, la risposta è no. Tuttavia, è possibile ottenere qualcosa di abbastanza simile sotto forma di una piccola unità che si collega al programma, la libreria AsyncCalls di Andreas Hausladen. Non è un componente, quindi credo che si qualifica. Supporta anche Delphi dalla versione 5 in poi. Molto consigliato

Edit:

io aggiungo un esempio in quanto non hai in esecuzione. Se si ottiene il blocco del codice chiamante, il problema è che non viene mantenuto alcun riferimento al puntatore all'interfaccia IAsyncCall restituito dalla funzione. L'oggetto che implementa l'interfaccia verrà quindi distrutto immediatamente quando il riferimento temporaneo non rientra nell'ambito. Il distruttore verrà chiamato nel contesto del thread VCL e chiamerà WaitForSingleObject() o una funzione simile per attendere il completamento del thread worker. Il risultato è che il tuo thread VCL blocca.

otterrete il comportamento corretto se si mantiene un riferimento al puntatore di interfaccia:

type 
    TForm1 = class(TForm) 
    Button1: TButton; 
    Timer1: TTimer; 
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); 
    procedure Button1Click(Sender: TObject); 
    procedure Timer1Timer(Sender: TObject); 
    private 
    fAsyncCall: IAsyncCall; 
    procedure WaitForIt(ADelay: integer); 
    end; 

Impostare il timer essere disabilitato e lasciare che hanno un tempo molto breve Interval, dicono 50 ms. Il clic del pulsante avvia l'operazione asincrona:

procedure TForm1.Button1Click(Sender: TObject); 
begin 
    Button1.Enabled := FALSE; 
    fAsyncCall := AsyncCall(WaitForIt, 1000); 
end; 

procedure TForm1.WaitForIt(ADelay: integer); 
begin 
    Sleep(ADelay); 

    EnterMainThread; 
    try 
    Randomize; 
    Color := RGB(Random(256), Random(256), Random(256)); 
    Timer1.Enabled := TRUE; 
    finally 
    LeaveMainThread; 
    end; 
end; 

Mentre l'operazione è attiva, non è possibile avviare altro. Al termine permette al timer per informare la forma e reimpostare il riferimento di interfaccia:

procedure TForm1.Timer1Timer(Sender: TObject); 
begin 
    Timer1.Enabled := FALSE; 
    Assert((fAsyncCall <> nil) and fAsyncCall.Finished); 
    fAsyncCall := nil; 
    Button1.Enabled := TRUE; 
end; 

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean); 
begin 
    CanClose := (fAsyncCall = nil) or fAsyncCall.Finished; 
end; 

Nota come sia ancora possibile accedere al modulo direttamente dal metodo chiamato, utilizzando EnterMainThread() e LeaveMainThread().

Sopra il codice non è il minimo assoluto, è destinato a dimostrare solo alcune idee.

+0

Ho visto quella libreria ieri, ma volevo mantenere il codice minimo, penso che finirò per usarlo comunque. È una semplice unità .pas, giusto? – Sebastian

+0

Sì lo è, solo tra 2 e 3 kLOC, quindi è già abbastanza minimale. Non penso che questo possa essere battuto. L'interfaccia è estremamente semplice, tutta la parte pelosa è nella parte 'implementation' ;-) Assicurati di mantenere i riferimenti' IAsyncCall' fino al termine della chiamata, altrimenti l'ultimo decremento del conteggio dei riferimenti verrà bloccato. – mghie

+0

Sembra che questa sarà la soluzione migliore, grazie mille. – Sebastian

Problemi correlati