2010-08-10 12 views
23

Soy nuevo con estas cosas de Roscar en Delphi. entonces, estoy tratando de hacer una consulta simple que haga un poco de llamada para la base de datos y tome un poco de tiempo, así que quiero alertar al usuario de que hay un proceso en segundo plano y tengo que ser paciente.Rosca simple Muestra Delphi

Probé muchas muestras, pero ninguna de ellas me funciona, por favor, ¿alguien podría mostrarme una muestra simple que podría funcionar?

Yo sé que tengo que declarar un tipo de TThread, con Crear y Anular Ejecutar ... etc .. pero desde que estoy perdido ...

Utilizando Delphi 7, SQL Server 2005 y ADO, Windows XP sp3.-

Gracias.

+5

Ha intentado muchas muestras y ha fallado. Entonces, ¿qué le daría otro ejemplo? Debería hacer una pregunta más detallada. ¿Con qué tienes problema? Por cierto, este tutorial se ve bien: http://wiki.lazarus.freepascal.org/Multithreaded_Application_Tutorial – adf88

+1

Y una más, a veces es una mejor solución (porque es más simple) solo para llamar a Application.ProcessMessages de vez en cuando dentro de su proceso que consume mucho tiempo (por ejemplo, solo para actualizar alguna barra de progreso o reproducir alguna otra animación que muestre al usuario que la aplicación está ocupada). – adf88

+0

HI, adf88, estoy totalmente de acuerdo con usted. Pero el problema con las muestras es que realmente no entiendo todo sobre Thread, así que pensé que una muestra Simple podría dejar las cosas más claras para mí. De cualquier forma, resolví el problema con Application.Processmessages, Thanks –

Respuesta

28

Puede encontrar muchos ejemplos en la red de hilos. La única característica especial, si está utilizando conexiones ADO dentro del subproceso, es que no puede compartir la misma conexión.
Cada hilo debe crear su propia conexión, de lo contrario son iguales (deben seguir las mismas reglas que cualquier otro hilo.)

Un ejemplo que he utilizado es la siguiente:

TADOSQLThread = class(TThread) 
    private 
    FADOQ: TADOQuery; // Internal query 
    FSQL: string;  // SQL To execute 
    FID: integer;  // Internal ID 

    public 
    constructor Create(CreateSuspended:Boolean; AConnString:String; 
         ASQL:string; IDThread:integer); 
    destructor Destroy; override; 
    procedure Execute(); override; 

    property ID:integer read FID write FID; 
    property SQL:string read FSQL write FSQL; 
    property ADOQ:TADOQuery read FADOQ write FADOQ; 
    end; 

El constructor Crear se overrided, y se parece a esto:

constructor TADOSQLThread.Create(CreateSuspended:Boolean; AConnString:String; 
           ASQL:string; IDThread:integer); 
begin 

    inherited Create(CreateSuspended); 

    // ini 
    Self.FreeOnTerminate := False; 

    // Create the Query 
    FADOQ := TAdoquery.Create(nil); 
    // assign connections 
    FADOQ.ConnectionString := AConnString; 
    FADOQ.SQL.Add(ASQL); 
    Self.FID := IDThread; 
    Self.FSQL:= ASQL; 
end; 

Y el método de ejecución es muy simple:

procedure TADOSQLThread.Execute(); 
begin 

    inherited; 

    try 
    // Ejecutar la consulta 
    Self.FADOQ.Open; 
    except 
    // Error al ejecutar 
    ...Error treattement 
    end; 
end; 

Para iniciar y crear un hilo, puede usar un código similar al siguiente:

//crear el Thread 
    th := TADOSQLThread.Create(True, mmConnection.Lines.Text, ASQL, AId); 
    // internal for me (for controled the number of active threads and limete it) 
    inc(numThreads); 
    // evento finalizacion 
    th.OnTerminate := TerminateThread; 
    // Ejecutarlo 
    th.Resume; 

Tengo crear un método TerminateThread que reciben el control de roscas cuando terminen. Lo único diferente a otros hilos es el problema de conexión. Debe crear una nueva conexión en cada hilo. No puede compartir las mismas ADOConnections con otros.
Espero que este ejemplo sea útil para usted.

Saludos

46

Sí, se declara un nuevo tipo que hereda de TThread:

TMyWorkerThread = class(TThread) 
end; 

a continuación, agregar una anulación de la función de Ejecutar():

TMyWorkerThread = class(TThread) 
public 
    procedure Execute; override; 
end; 

Este procedimiento se llamará cuando se inicia el hilo. Se ejecutará en paralelo con su programa principal. Vamos a escribirlo.

procedure TMyWorkerThread.Execute; 
begin 
    //Here we do work 
    DoSomeWork(); 
    DoMoreWork(); 
    //When we exit the procedure, the thread ends. 
    //So we don't exit until we're done. 
end; 

¿Cómo utilizar esto? Supongamos que desea comenzar a trabajar cuando el usuario hace clic en el botón. Usted escribe un controlador OnClick:

procedure TMainForm.Button1Click(Sender: TObject); 
begin 
    TMyWorkerThread.Create(false); 
end; 

Eso es todo. Después de que el usuario haga clic en el botón, el hilo comienza y continúa haciendo lo que sea que haya escrito en Ejecutar. Si el usuario vuelve a hacer clic en el botón, se iniciará otro, y luego otro, uno por cada clic. Todos se ejecutarán en paralelo, cada uno haciendo todo lo que está escrito en Execute() y luego terminando.

Digamos que desea comprobar si el trabajo ha finalizado. Por eso, usted tiene que almacenar la referencia a su hilo de alguna parte:

TMainForm = class(TForm) 
{...skipped...} 
public 
    MyWorkerThread: TThread; 
end; 

procedure TMainForm.Button1Click(Sender: TObject); 
begin 
    //This time we make sure only one thread can be started. 
    //If one thread have been started already, we don't start another. 
    if MyWorkerThread<>nil then 
    raise Exception.Create('One thread have already been started!'); 
    MyWorkerThread := TMyWorkerThread.Create(false); 
end; 

procedure TMainForm.Button2Click(Sender: TObject); 
begin 
    //If the work is not over yet, we display message informing the user we're still working 
    if (MyWorkerThread<>nil) and (WaitForSingleObject(MyWorkerThread.Handle, 0)<>WAIT_OBJECT_0) then 
    MessageBox(Self.Handle, pchar("The work is not yet done!"), pchar("Still running"), MB_OK); 
end; 

Como se ve, estamos comprobando si un hilo sigue corriendo llamando a una función de Windows llamada WaitForSingleObject. Esta función espera hasta que el hilo termine de funcionar, o el tiempo de espera haya expirado, y cuando especificamos el tiempo de espera de 0, simplemente existe inmediatamente si el hilo no se ha terminado aún.

+2

Conozco este código de ejemplo, pero un comentario que indica lo que sucede si el usuario hace clic en el botón más de una vez sería útil. –

+0

¿No debería ese "TThread" en la última definición de bloque de código ser "MyWorkerThread: TMyWorkerThread" en su lugar? –

+0

@ThatMarc: La idea aquí era que no necesitas saber qué trabajo particular hace TMyWorkerThread para saber que el hilo ha terminado. Cualquier descendiente de TThread se puede marcar así. – himself