我一個線程通常的設置是一個while循環和while循環中做兩件事情:如何終止一個線程?
- 做了一些工作
- 暫停,直到從外部
procedure TMIDI_Container_Publisher.Execute;
begin
Suspend;
while not Terminated do
begin
FContainer.Publish;
if not Terminated then Suspend;
end; // if
end; // Execute //
恢復這工作正常。要終止我使用的代碼:
destructor TMIDI_Container_Publisher.Destroy;
begin
Terminate;
if Suspended then Resume;
Application.ProcessMessages;
Self.WaitFor;
inherited Destroy;
end; // Destroy //
此Destroy在Windows 7中正常工作,但在XP中掛起。問題似乎是WaitFor,但是當我刪除它時,代碼掛起在inherited Destroy
。
任何人的想法是什麼錯?
更新2011/11/02 感謝大家的幫助。 Remy Labeau提供了一個代碼示例來避免Resume/Suspend。我將從現在開始在我的程序中實施他的建議。對於這個特定的情況,我受到了CodeInChaos的建議的啓發。只需創建一個線程,讓它在Execute中進行發佈並忘記它。我用Remy的例子來重寫我的一個定時器。我在下面發佈這個實現。
unit Timer_Threaded;
interface
uses Windows, MMSystem, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, SyncObjs,
Timer_Base;
Type
TTask = class (TThread)
private
FTimeEvent: TEvent;
FStopEvent: TEvent;
FOnTimer: TNotifyEvent;
public
constructor Create;
destructor Destroy; override;
procedure Execute; override;
procedure Stop;
procedure ProcessTimedEvent;
property OnTimer: TNotifyEvent read FOnTimer write FOnTimer;
end; // Class: TWork //
TThreadedTimer = class (TBaseTimer)
private
nID: cardinal;
FTask: TTask;
protected
procedure SetOnTimer (Task: TNotifyEvent); override;
procedure StartTimer; override;
procedure StopTimer; override;
public
constructor Create; override;
destructor Destroy; override;
end; // Class: TThreadedTimer //
implementation
var SelfRef: TTask; // Reference to the instantiation of this timer
procedure TimerUpdate (uTimerID, uMessage: cardinal; dwUser, dw1, dw2: cardinal); stdcall;
begin
SelfRef.ProcessTimedEvent;
end; // TimerUpdate //
{*******************************************************************
* *
* Class TTask *
* *
********************************************************************}
constructor TTask.Create;
begin
FTimeEvent := TEvent.Create (nil, False, False, '');
FStopEvent := TEvent.Create (nil, True, False, '');
inherited Create (False);
Self.Priority := tpTimeCritical;
end; // Create //
destructor TTask.Destroy;
begin
Stop;
FTimeEvent.Free;
FStopEvent.Free;
inherited Destroy;
end; // Destroy //
procedure TTask.Execute;
var two: TWOHandleArray;
h: PWOHandleArray;
ret: DWORD;
begin
h := @two;
h [0] := FTimeEvent.Handle;
h [1] := FStopEvent.Handle;
while not Terminated do
begin
ret := WaitForMultipleObjects (2, h, FALSE, INFINITE);
if ret = WAIT_FAILED then Break;
case ret of
WAIT_OBJECT_0 + 0: if Assigned (OnTimer) then OnTimer (Self);
WAIT_OBJECT_0 + 1: Terminate;
end; // case
end; // while
end; // Execute //
procedure TTask.ProcessTimedEvent;
begin
FTimeEvent.SetEvent;
end; // ProcessTimedEvent //
procedure TTask.Stop;
begin
Terminate;
FStopEvent.SetEvent;
WaitFor;
end; // Stop //
{*******************************************************************
* *
* Class TThreaded_Timer *
* *
********************************************************************}
constructor TThreadedTimer.Create;
begin
inherited Create;
FTask := TTask.Create;
SelfRef := FTask;
FTimerName := 'Threaded';
Resolution := 2;
end; // Create //
// Stop the timer and exit the Execute loop
Destructor TThreadedTimer.Destroy;
begin
Enabled := False; // stop timer (when running)
FTask.Free;
inherited Destroy;
end; // Destroy //
procedure TThreadedTimer.SetOnTimer (Task: TNotifyEvent);
begin
inherited SetOnTimer (Task);
FTask.OnTimer := Task;
end; // SetOnTimer //
// Start timer, set resolution of timesetevent as high as possible (=0)
// Relocates as many resources to run as precisely as possible
procedure TThreadedTimer.StartTimer;
begin
nID := TimeSetEvent (FInterval, FResolution, TimerUpdate, cardinal (Self), TIME_PERIODIC);
if nID = 0 then
begin
FEnabled := False;
raise ETimer.Create ('Cannot start TThreaded_Timer');
end; // if
end; // StartTimer //
// Kill the system timer
procedure TThreadedTimer.StopTimer;
var return: integer;
begin
if nID <> 0 then
begin
return := TimeKillEvent (nID);
if return <> TIMERR_NOERROR
then raise ETimer.CreateFmt ('Cannot stop TThreaded_Timer: %d', [return]);
end; // if
end; // StopTimer //
end. // Unit: MSC_Threaded_Timer //
unit Timer_Base;
interface
uses
Windows, MMSystem, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs;
type
TCallBack = procedure (uTimerID, uMessage: UINT; dwUser, dw1, dw2: DWORD);
ETimer = class (Exception);
{$M+}
TBaseTimer = class (TObject)
protected
FTimerName: string; // Name of the timer
FEnabled: boolean; // True= timer is running, False = not
FInterval: Cardinal; // Interval of timer in ms
FResolution: Cardinal; // Resolution of timer in ms
FOnTimer: TNotifyEvent; // What to do when the hour (ms) strikes
procedure SetEnabled (value: boolean); virtual;
procedure SetInterval (value: Cardinal); virtual;
procedure SetResolution (value: Cardinal); virtual;
procedure SetOnTimer (Task: TNotifyEvent); virtual;
protected
procedure StartTimer; virtual; abstract;
procedure StopTimer; virtual; abstract;
public
constructor Create; virtual;
destructor Destroy; override;
published
property TimerName: string read FTimerName;
property Enabled: boolean read FEnabled write SetEnabled;
property Interval: Cardinal read FInterval write SetInterval;
property Resolution: Cardinal read FResolution write SetResolution;
property OnTimer: TNotifyEvent read FOnTimer write SetOnTimer;
end; // Class: HiResTimer //
implementation
constructor TBaseTimer.Create;
begin
inherited Create;
FEnabled := False;
FInterval := 500;
Fresolution := 10;
end; // Create //
destructor TBaseTimer.Destroy;
begin
inherited Destroy;
end; // Destroy //
// SetEnabled calls StartTimer when value = true, else StopTimer
// It only does so when value is not equal to the current value of FEnabled
// Some Timers require a matching StartTimer and StopTimer sequence
procedure TBaseTimer.SetEnabled (value: boolean);
begin
if value <> FEnabled then
begin
FEnabled := value;
if value
then StartTimer
else StopTimer;
end; // if
end; // SetEnabled //
procedure TBaseTimer.SetInterval (value: Cardinal);
begin
FInterval := value;
end; // SetInterval //
procedure TBaseTimer.SetResolution (value: Cardinal);
begin
FResolution := value;
end; // SetResolution //
procedure TBaseTimer.SetOnTimer (Task: TNotifyEvent);
begin
FOnTimer := Task;
end; // SetOnTimer //
end. // Unit: MSC_Timer_Custom //
繼承的destroy會調用WaitFor。不知道你的問題,但你不應該使用暫停或恢復。我會使用事件來暫停線程。 ProcessMessages是做什麼的? –
我們不知道'繼承的Destroy'中有什麼,所以很難說。但按照慣例,不應使用「暫停」和「恢復」。最好使用同步對象(嘗試'SyncObjs.TSimpleEvent')並讓線程等待它。 –
@Mason因爲我們正在調用'Terminate','Resume','Suspend','WaitFor'等等,我認爲我們可以猜測'inherited Destroy'是'TThread.Destroy'。但是你對'暫停','恢復'和事件是完全正確的。 –