【发布时间】:2011-01-07 00:38:39
【问题描述】:
以前有人问过,但是without a full answer。这与所谓的著名的“‘致命线程模型!’”有关。
我需要用安全的东西替换对 TThread.Suspend 的调用,它会在终止或恢复时返回:
procedure TMyThread.Execute;
begin
while (not Terminated) do begin
if PendingOffline then begin
PendingOffline := false; // flag off.
ReleaseResources;
Self.Suspend; // suspend thread. { evil! ask Barry Kelly why.}
// -- somewhere else, after a long time, a user clicks
// a resume button, and the thread resumes: --
if Terminated then
exit; // leave TThread.Execute.
// Not terminated, so we continue..
GrabResources;
end;
end;
end;
原始答案含糊地暗示“TMutex、TEvent 和临界区”。
我想我正在寻找 TThreadThatDoesntSuck。
这是用于 cmets 的带有 Win32Event 的 TThread 派生示例:
unit SignalThreadUnit;
interface
uses
Classes,SysUtils,Windows;
type
TSignalThread = class(TThread)
protected
FEventHandle:THandle;
FWaitTime :Cardinal; {how long to wait for signal}
//FCritSec:TCriticalSection; { critical section to prevent race condition at time of change of Signal states.}
FOnWork:TNotifyEvent;
FWorkCounter:Cardinal; { how many times have we been signalled }
procedure Execute; override; { final; }
//constructor Create(CreateSuspended: Boolean); { hide parent }
public
constructor Create;
destructor Destroy; override;
function WaitForSignal:Boolean; { returns TRUE if signal received, false if not received }
function Active:Boolean; { is there work going on? }
property WorkCounter:Cardinal read FWorkCounter; { how many times have we been signalled }
procedure Sync(AMethod: TThreadMethod);
procedure Start; { replaces method from TThread }
procedure Stop; { provides an alternative to deprecated Suspend method }
property Terminated; {make visible}
published
property WaitTime :Cardinal read FWaitTime write FWaitTime; {how long to wait for signal}
property OnWork:TNotifyEvent read FOnWork write FOnWork;
end;
implementation
{ TSignalThread }
constructor TSignalThread.Create;
begin
inherited Create({CreateSuspended}true);
// must create event handle first!
FEventHandle := CreateEvent(
{security} nil,
{bManualReset} true,
{bInitialState} false,
{name} nil);
FWaitTime := 10;
end;
destructor TSignalThread.Destroy;
begin
if Self.Suspended or Self.Terminated then
CloseHandle(FEventHandle);
inherited;
end;
procedure TSignalThread.Execute;
begin
// inherited; { not applicable here}
while not Terminated do begin
if WaitForSignal then begin
Inc(FWorkCounter);
if Assigned(FOnWork) then begin
FOnWork(Self);
end;
end;
end;
OutputDebugString('TSignalThread shutting down');
end;
{ Active will return true when it is easily (instantly) apparent that
we are not paused. If we are not active, it is possible we are paused,
or it is possible we are in some in-between state. }
function TSignalThread.Active: Boolean;
begin
result := WaitForSingleObject(FEventHandle,0)= WAIT_OBJECT_0;
end;
procedure TSignalThread.Start;
begin
SetEvent(FEventHandle); { when we are in a signalled state, we can do work}
if Self.Suspended then
inherited Start;
end;
procedure TSignalThread.Stop;
begin
ResetEvent(FEventHandle);
end;
procedure TSignalThread.Sync(AMethod: TThreadMethod);
begin
Synchronize(AMethod);
end;
function TSignalThread.WaitForSignal: Boolean;
var
ret:Cardinal;
begin
result := false;
ret := WaitForSingleObject(FEventHandle,FWaitTime);
if (ret=WAIT_OBJECT_0) then
result := not Self.Terminated;
end;
end.
【问题讨论】:
-
对于
TThreadThatDoesntSuck,您应该查看 OmniThreadLibrary,otl.17slon.com -
在“Self.Suspend”发生之前执行“线程恢复”(thread.Resume)的代码会发生什么?您真正想要的是手动重置事件,该事件设置为未发出信号,在您要暂停的位置等待它,然后在您要恢复的位置设置事件。这样一来,您就不会因为太晚暂停而错过简历。
-
我认为巴里的评论非常接近我的回答。我编辑了问题以显示暂停(阻塞)线程的“无挂起”方式的示例实现,直到我希望它再次用完一些 CPU 时间。基本上我想要一个“开始”和“停止”抽象。在应用程序结束之前,线程不应该终止,我们正在清理。
-
在 dot-net 环境中创建“可挂起线程”的有趣相关尝试表明,Microsoft 还放弃并取消了 Thread.Suspend 方法:msmvps.com/blogs/peterritchie/archive/2006/10/13/…2700_Thread.Suspend-has -been-deprecated_2E002E002E00.aspx