我的通常线程设置是一个 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 //
inherited Destroy
中有什么,所以很难说。但作为一般惯例,不应该使用Suspend
和Resume
。最好使用同步对象(尝试使用SyncObjs.TSimpleEvent
),并让您的线程在其上等待。 - Mason Wheelerinherited Destroy
会让事情变得更好。由于它执行与你的析构函数相同的所有操作,你仍然会受到相同的竞态条件的影响。(此外,如果一个方法——即使是析构函数——除了调用继承的方法之外什么也不做,你根本不需要编写任何内容;完全省略派生类方法。) - Rob Kennedy