2009-12-02 51 views

Respuesta

4

Es posible que también desee ejecutar su procedimiento en un hilo. Use luego el evento OnTerminate para obtener el resultado. Sí, en estos días de .NET y C# estamos algo mimados con la forma fácil y conveniente de ejecutar métodos asincrónicamente, pero así es como funciona en Delphi.

+0

Tengo una versión de lo que estoy haciendo en C# jeje, ahora que estoy tratando de traducir a delphi (porque no quiero usar .NET) veo lo que estás diciendo. Gracias. – Sebastian

+0

Finalmente lo tengo trabajando con un simple BeginThread solamente, realmente no me importa el resultado del procedimiento, solo tiene que hacer algo o Salir. Gracias por su mensaje – Sebastian

16

Si está preguntando si el VCL tiene algo like BeginInvoke in .NET listo para usar, entonces la respuesta es no. Sin embargo, puede obtener algo muy similar en la forma de una pequeña unidad que vincula a su programa, la biblioteca AsyncCalls de Andreas Hausladen. No es un componente, así que supongo que califica. También es compatible con Delphi desde la versión 5 en adelante. Muy recomendado

Editar:

Voy a añadir un ejemplo ya que usted no ponerlo en marcha. Si obtiene un bloqueo en su código de llamada, entonces su problema es que no se mantiene ninguna referencia al puntero de interfaz IAsyncCall que devolvió la función. El objeto que implementa la interfaz se destruirá inmediatamente cuando la referencia temporal salga del alcance. Se llamará al destructor en el contexto del hilo VCL, y llamará al WaitForSingleObject() o una función similar para esperar a que termine el hilo del trabajador. El resultado de eso es que su hilo VCL bloquea.

obtendrá el comportamiento correcto si se mantiene una referencia al puntero de interfaz:

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; 

Ajuste el temporizador debe ser inhabilitado y hacer que tenga un tiempo muy corto Interval, dicen que 50 ms. El clic de botón inicia la operación asíncrona:

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; 

Mientras la operación está activa no se puede iniciar ninguna otra. Al finalizar el temporizador que permite notificar a la forma y restablecer la referencia de interfaz:

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 cómo es posible incluso para acceder al formulario directamente desde el método llamado, mediante el uso de EnterMainThread() y LeaveMainThread().

El código anterior no es el mínimo absoluto, solo pretende demostrar algunas ideas.

+0

Vi esa biblioteca ayer, pero quería mantener el código mínimo, creo que terminaré utilizándolo de todos modos. Es una unidad .pas simple ¿verdad? – Sebastian

+0

Sí, lo es, solo entre 2 y 3 kLOC, por lo que ya es bastante mínimo. No creo que esto sea inmejorable. La interfaz es extremadamente sencilla, todas las cosas peludas están en la parte de 'implementación' ;-) Solo asegúrate de mantener las referencias 'IAsyncCall' hasta que la llamada haya finalizado, de lo contrario se bloqueará la última disminución del recuento de referencia. – mghie

+0

Parece que esta será la mejor solución, muchas gracias. – Sebastian

Cuestiones relacionadas