tonr ton tof temporizadores temporizador entre diferencia multithreading delphi delphi-xe

multithreading - tof - temporizador tonr



Delphi: el temporizador dentro de la secuencia genera AV (5)

¿Puedes verificar si el temporizador es realmente propiedad del nuevo hilo (TProcess) o del principal? Los temporizadores en Windows son "propiedad" (en términos del administrador de recursos) por hilos, no por procesos. Si su temporizador es propiedad del hilo principal, entonces el evento OnTimer se ejecutará en el contexto del hilo principal, e incluso si llama explícitamente a Execute, la llamada seguirá estando en el contexto del hilo principal, sin importar si Execute es un "procedimiento de objeto" que pasa a ser un descendiente TThread.

Y no puede llamar explícitamente a Execute de todos modos. Este procedimiento se llama (en el contexto del nuevo hilo) cuando se ejecuta el hilo.

Mejor intente esto: Ejecute dentro, cree el temporizador usando las funciones api de Windows, y espere infinitamente (SleepEx) con el parámetro alertable establecido en TRUE. Entonces, el temporizador disparará en el contexto del nuevo hilo. Alternativamente, en el evento OnTimer (en el contexto del hilo principal) puede estar enviando llamadas al procedimiento APC al hilo del trabajador (todavía tendrá que esperar en SleepEx y establecer alertable a TRUE). Una alternativa completamente diferente: en el evento OnTimer cree el objeto thread y realice el procesamiento normal dentro de Execute - FreeOnTerminate debe establecerse en true para que el objeto se libere después de finalizar.

Y una nota final, no estoy seguro de si puede pasar esa función EnumProcess (una función declarada dentro de un "procedimiento de objeto" ???) a una llamada de WinApi. Esto bien puede estar causando los bloqueos. Creo que necesitas una función declarada a nivel global.

Tengo el siguiente código de subproceso que se ejecuta correctamente la primera vez. Después de eso, de vez en cuando obtengo un AV en el método Execute del hilo, por ej.

Salida de depuración: violación de TProcesses.Execute Access en la dirección 00409C8C en el módulo ''ListenOutputDebugString.exe''. Lectura de la dirección 08070610 Proceso ListenOutputDebugString.exe (740)

No sé qué está generando este AV ...

unit Unit3; interface uses Classes, StdCtrls, Windows, ExtCtrls, SysUtils, Variants, JvExGrids, JvStringGrid; type TProcesses = class(TThread) private { Private declarations } FTimer : TTimer; FGrid : TJvStringGrid; FJobFinished : Boolean; procedure OverrideOnTerminate(Sender: TObject); procedure DoShowData; procedure DoShowErrors; procedure OverrideOnTimer(Sender: TObject); protected procedure Execute; override; public constructor Create(aGrid : TJvStringGrid);overload; end; implementation {TProcesses } var SharedMessage : String; ErrsMess : String; lp : Integer; constructor TProcesses.Create(aGrid : TJvStringGrid); begin FreeOnTerminate := True; FTimer := TTimer.Create(nil); FTimer.OnTimer := OverrideOnTerminate; FTimer.OnTimer := OverrideOnTimer; FTimer.Interval := 10000; FGrid := aGrid; inherited Create(false); FTimer.Enabled := true; FJobFinished := true; end; procedure TProcesses.DoShowData; var wStrList : TStringList; wi,wj : Integer; begin // FMemo.Lines.Clear; for wi := 1 to FGrid.RowCount-1 do for wj := 0 to FGrid.ColCount-1 do FGrid.Cells[wj,wi] := ''''; try try wStrList := TStringList.Create; wStrList.Delimiter := '';''; wStrList.StrictDelimiter := true; wStrList.DelimitedText := SharedMessage; // outputdebugstring(PChar(''Processes list ''+SharedMessage)); FGrid.RowCount := wStrList.Count div 4; for wi := 0 to wStrList.Count-1 do FGrid.Cells[(wi mod 4), (wi div 4)+1] := wStrList[wi]; Except on e:Exception do OutputDebugString(Pchar(''TProcesses.DoShowData ''+e.Message)); end; finally FreeAndNil(wStrList); end; end; procedure TProcesses.DoShowErrors; begin // FMemo.Lines.Add(''Error ''+ ErrsMess); FGrid.Cells[1,1] := ''Error ''+ ErrsMess; ErrsMess := ''''; end; procedure TProcesses.Execute; function EnumProcess(hHwnd: HWND; lParam : integer): boolean; stdcall; var pPid : DWORD; title, ClassName : string; begin //if the returned value in null the //callback has failed, so set to false and exit. if (hHwnd=NULL) then begin result := false; end else begin //additional functions to get more //information about a process. //get the Process Identification number. GetWindowThreadProcessId(hHwnd,pPid); //set a memory area to receive //the process class name SetLength(ClassName, 255); //get the class name and reset the //memory area to the size of the name SetLength(ClassName, GetClassName(hHwnd, PChar(className), Length(className))); SetLength(title, 255); //get the process title; usually displayed //on the top bar in visible process SetLength(title, GetWindowText(hHwnd, PChar(title), Length(title))); //Display the process information //by adding it to a list box SharedMessage := SharedMessage + (className +'' ;''+//''Class Name = '' + title +'' ;''+//''; Title = '' + IntToStr(hHwnd) +'' ;''+ //''; HWND = '' + IntToStr(pPid))+'' ;''//''; Pid = '' + ;// +#13#10; Result := true; end; end; begin if FJobFinished then begin try FJobFinished := false; //define the tag flag lp := 0; //globally declared integer //call the windows function with the address //of handling function and show an error message if it fails SharedMessage := ''''; if EnumWindows(@EnumProcess,lp) = false then begin ErrsMess := SysErrorMessage(GetLastError); Synchronize(DoShowErrors); end else Synchronize(DoShowData); FJobFinished := true; Except on e:Exception do OutputDebugString(Pchar(''TProcesses.Execute ''+e.Message)); end; end end; procedure TProcesses.OverrideOnTerminate(Sender: TObject); begin FTimer.Enabled := false; FreeAndNil(FTimer); end; procedure TProcesses.OverrideOnTimer(Sender: TObject); begin Self.Execute; end; end.


Nunca usaría el temporizador en un hilo. En su lugar, crearía un evento del sistema y lo esperaría en el ciclo de ejecución del subproceso durante un tiempo específico con la función WaitForSingleObject . Esta función espera hasta que el objeto especificado (en este caso, el evento) se encuentre en estado señalizado o transcurra el intervalo de tiempo de espera.

El principio es fácil, creará el evento en el estado no señalado y lo mantendrá en ese estado hasta que el hilo termine. Esto hará que la función WaitForSingleObject tiempo de espera cada vez que bloquea el ciclo de ejecución de la secuencia durante el tiempo especificado en la llamada a la función. Una vez que decida terminar su hilo, simplemente establezca el indicador de finalización del hilo (sobre el cual debe preguntar tanto como pueda) y establezca ese evento en el estado señalado, lo que hace que la función WaitForSingleObject regrese inmediatamente.

Aquí hay un ejemplo que simula un temporizador de subprocesos (con un intervalo de 2 segundos = 2000 ms utilizados como un segundo parámetro en las llamadas a la función WaitForSingleObject ):

unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs; type TTimerThread = class(TThread) private FTickEvent: THandle; protected procedure Execute; override; public constructor Create(CreateSuspended: Boolean); destructor Destroy; override; procedure FinishThreadExecution; end; type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private FTimerThread: TTimerThread; public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin ReportMemoryLeaksOnShutdown := True; FTimerThread := TTimerThread.Create(False); end; procedure TForm1.FormDestroy(Sender: TObject); begin FTimerThread.FinishThreadExecution; end; { TTimerThread } constructor TTimerThread.Create(CreateSuspended: Boolean); begin inherited; FreeOnTerminate := True; FTickEvent := CreateEvent(nil, True, False, nil); end; destructor TTimerThread.Destroy; begin CloseHandle(FTickEvent); inherited; end; procedure TTimerThread.FinishThreadExecution; begin Terminate; SetEvent(FTickEvent); end; procedure TTimerThread.Execute; begin while not Terminated do begin if WaitForSingleObject(FTickEvent, 2000) = WAIT_TIMEOUT then begin Synchronize(procedure begin Form1.Tag := Form1.Tag + 1; Form1.Caption := IntToStr(Form1.Tag); end ); end; end; end; end.


Primero, en el constructor TProcesses.Create (aGrid: TJvStringGrid); tienes:

FTimer.OnTimer := OverrideOnTerminate; FTimer.OnTimer := OverrideOnTimer;

Aquí OverrideOnTerminate nunca se dispara. Probablemente quieras atrapar hilo OnTerminate.

En segundo lugar, crea un hilo en estado de ejecución heredado Create (falso); entonces Execute se llama automáticamente. Cuando Execute finaliza, llama a DoTerminate y se destruye el hilo.

Luego, cuando el temporizador enciende OnTimer, usted llama varias veces Execute; Aquí el hilo ya no existe. El temporizador no se libera e intenta iniciar un hilo muerto.

Necesita volver a escribir su código siguiendo algunas reglas:

  1. Execute debería ejecutarse continuamente. Puede poner hilo para "dormir" usando WaitForSingleObject / WaitForMultipleObjects. Eche un vistazo a la ayuda de MSDN.
  2. Estas funciones tienen el parámetro Timeout, por lo que no necesita TTimer en absoluto.

[EDIT] Encontré una muestra útil para ti (lo siento, no la he probado):

procedure TProcesses.Execute; const _SECOND = 10000000; var lBusy : LongInt; hTimer : LongInt; liWaitTime : LARGE_INTEGER; begin hTimer := CreateWaitableTimer(nil, True, ''WaitableTimer''); liWaitTime.QuadPart := _SECOND * YOUR_NumberOfSeconds; SetWaitableTimer(hTimer, TLargeInteger(liWaitTime ), 0, nil, nil, False); repeat lBusy := MsgWaitForMultipleObjects(1, hTimer, False, INFINITE, QS_ALLINPUT); // CODE EXECUTED HERE EVERY YOUR_NumberOfSeconds Until lBusy = WAIT_OBJECT_0; CloseHandle(hTimer); end;

Necesitas ajustar esto ligeramente. Agregue un objeto más para esperar: un evento creado con la función CreateEvent. Cuando necesite finalizar el hilo instantáneamente solo llame a la función SetEvent.


Su hilo está trabajando en controles GUI (Asumiendo que TJvStringGrid es un control GUI). Esa nunca es una buena idea y puede dar resultados inesperados. No hay otro hilo, entonces el hilo principal debe tocar las cosas de la GUI.


TTimer no es seguro para subprocesos. Período. Ni siquiera intentes utilizarlo con un hilo de trabajo.

Está creando instancias del TTimer en el constructor del subproceso de trabajo, lo que significa que se crea una instancia en el contexto del subproceso que está creando el subproceso de trabajo, no en el contexto del subproceso de trabajo mismo. Eso también significa que el temporizador se ejecutará en ese mismo contexto de subproceso y que el OnTimer eventos OnTimer no se activará en el contexto de la cadena de trabajo (si es que lo hace), por lo que el cuerpo de su controlador OnTimer debe ser seguro para subprocesos.

Para que el evento TTimer.OnTimer se desencadene en el contexto del subproceso de trabajo, debe crear una instancia del TTimer dentro del método Execute() del subproceso. Pero eso tiene otro conjunto de trampas. TTimer crea una ventana oculta utilizando AllocateHWnd() , que no es seguro para subprocesos y no se puede usar de forma segura fuera del contexto del hilo principal. Además, TTimer requiere que el contexto de la TTimer creación tenga un bucle de mensaje activo, que no es el hilo.

Para hacer lo que está intentando, debe cambiar a usar la función Win32 API SetTimer() directamente (lo que le permite omitir la necesidad de una ventana) y luego agregar un bucle de mensaje a su hilo (que aún necesita si use una ventana o no), o cambie a un mecanismo de sincronización diferente. Puede usar un temporizador de espera mediante CreateWaitableTimer() y WaitForSingleObject() , en cuyo caso no necesita una ventana o un mensaje loopp. O puede usar un temporizador multimedia a través de timeSetEvent() (solo asegúrese de que la devolución de llamada del temporizador multimedia esté protegida contra subprocesos porque el temporizador se ejecutará en su propio hilo).