问题的原因在于Thread不应该自己启动。
线程永远不知道初始化何时完成。构造函数与初始化不同(构造函数应该始终简短且无异常;进一步的初始化在构造函数之后进行)。
相似的情况是TDataSet:任何一个TDataSet构造函数都不应该调用Open或将Active设置为True。
请参阅Wings of Wind的博客文章。
你应该采取以下措施之一:
- 通过调用Create(true)创建一个被挂起的TMyThread,并在TMyThread类之外执行Start。
- 创建非挂起的TMyThread,确保Create构造函数进行完整的初始化,并让TThread.AfterConstruction启动线程。
TThread用法说明:
基本上,线程应该只是代码执行的上下文封装。
实际执行的业务逻辑代码应该在其他类中。
通过解耦这两者,您可以获得很大的灵活性,特别是从多个地方启动业务逻辑(在编写单元测试时非常方便!)。
这是您可以使用的框架类型:
unit DecoupledThreadUnit;
interface
uses
Classes;
type
TDecoupledThread = class(TThread)
strict protected
procedure DoExecute; virtual;
procedure DoSetUp; virtual;
procedure DoTearDown; virtual;
protected
procedure DoTerminate; override;
procedure Execute; override;
public
constructor Create;
procedure AfterConstruction; override;
end;
implementation
constructor TDecoupledThread.Create;
begin
inherited Create(True);
end;
procedure TDecoupledThread.AfterConstruction;
begin
DoSetUp();
inherited AfterConstruction;
end;
procedure TDecoupledThread.DoExecute;
begin
end;
procedure TDecoupledThread.DoSetUp;
begin
end;
procedure TDecoupledThread.DoTearDown;
begin
end;
procedure TDecoupledThread.DoTerminate;
begin
inherited DoTerminate();
DoTearDown();
end;
procedure TDecoupledThread.Execute;
begin
DoExecute();
end;
end.
你甚至可以通过以下方式使其基于事件:
unit EventedThreadUnit;
interface
uses
Classes,
DecoupledThreadUnit;
type
TCustomEventedThread = class(TDecoupledThread)
private
FOnExecute: TNotifyEvent;
FOnSetUp: TNotifyEvent;
FOnTearDown: TNotifyEvent;
strict protected
procedure DoExecute; override;
procedure DoSetUp; override;
procedure DoTearDown; override;
public
property OnExecute: TNotifyEvent read FOnExecute write FOnExecute;
property OnSetUp: TNotifyEvent read FOnSetUp write FOnSetUp;
property OnTearDown: TNotifyEvent read FOnTearDown write FOnTearDown;
end;
TEventedThread = class(TCustomEventedThread)
published
property OnExecute;
property OnSetUp;
property OnTearDown;
end;
implementation
procedure TCustomEventedThread.DoExecute;
var
TheOnExecute: TNotifyEvent;
begin
inherited;
TheOnExecute := OnExecute;
if Assigned(TheOnExecute) then
TheOnExecute(Self);
end;
procedure TCustomEventedThread.DoSetUp;
var
TheOnSetUp: TNotifyEvent;
begin
inherited;
TheOnSetUp := OnSetUp;
if Assigned(TheOnSetUp) then
TheOnSetUp(Self);
end;
procedure TCustomEventedThread.DoTearDown;
var
TheOnTearDown: TNotifyEvent;
begin
inherited;
TheOnTearDown := OnTearDown;
if Assigned(TheOnTearDown) then
TheOnTearDown(Self);
end;
end.
或者可以像这样为 DUnit TTestCase 的后代适配:
unit TestCaseThreadUnit;
interface
uses
DecoupledThreadUnit,
TestFramework;
type
TTestCaseRanEvent = procedure (Sender: TObject; const TestResult: TTestResult) of object;
TTestCaseThread = class(TDecoupledThread)
strict private
FTestCase: TTestCase;
strict protected
procedure DoTestCaseRan(const TestResult: TTestResult); virtual;
function GetTestCase: TTestCase; virtual;
procedure SetTestCase(const Value: TTestCase); virtual;
protected
procedure DoExecute; override;
procedure DoSetUp; override;
procedure DoTearDown; override;
public
constructor Create(const TestCase: TTestCase);
property TestCase: TTestCase read GetTestCase write SetTestCase;
end;
implementation
constructor TTestCaseThread.Create(const TestCase: TTestCase);
begin
inherited Create();
Self.TestCase := TestCase;
end;
procedure TTestCaseThread.DoExecute;
var
TestResult: TTestResult;
begin
if Assigned(TestCase) then
begin
TestResult := TestCase.Run();
try
DoTestCaseRan(TestResult);
finally
TestResult.Free;
end;
end
else
inherited DoExecute();
end;
procedure TTestCaseThread.DoTestCaseRan(const TestResult: TTestResult);
begin
end;
function TTestCaseThread.GetTestCase: TTestCase;
begin
Result := FTestCase;
end;
procedure TTestCaseThread.SetTestCase(const Value: TTestCase);
begin
FTestCase := Value;
end;
procedure TTestCaseThread.DoSetUp;
begin
if not Assigned(TestCase) then
inherited DoSetUp();
end;
procedure TTestCaseThread.DoTearDown;
begin
if not Assigned(TestCase) then
inherited DoTearDown();
end;
end.
--jeroen
TCustomEventedThread.DoExecute
等方法中要经过本地变量?这是一种虚假的安全感,因为事件不仅可以被重新分配,对象也可能被销毁,从而使保存的事件处理程序无效。总的来说,我认为基于接口的解决方案是安全地分离线程类和业务逻辑类的唯一方法,因为生命周期管理问题已得到解决。没有必要重新发明轮子,OTL已经存在,并在抽象化这些事物方面走得很远。 - mghie