【发布时间】:2011-12-19 02:36:53
【问题描述】:
我通常的线程设置是一个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。不知道您的问题,但您不应该使用 Suspend 或 Resume。我会使用事件来暂停线程。 ProcessMessages 有什么作用?
-
我们不知道
inherited Destroy里有什么,所以很难说。但作为一般做法,不应使用Suspend和Resume。最好使用同步对象(尝试SyncObjs.TSimpleEvent)并让您的线程等待它。 -
“FreeOnTerminate”设置为什么?
-
@warren 你还能怎么跑到完成?
-
有点像阿诺德。确实,析构函数中的代码是多余的,但这并不意味着
inherited Destroy会让事情变得更好。因为它和你的析构函数做同样的事情,你仍然容易受到所有相同的竞争条件的影响。 (此外,如果一个方法——即使是析构函数——只调用继承的方法,你根本不需要写任何东西;完全省略后代方法。)
标签: multithreading delphi