问题描述
在D2010,TThread的简历方法已弃用。所以,我以为现在应该这样工作:
TThread's resume method is deprecated in D2010. So, I thought it should now work like this:
TMyThread = class (TThread)
protected
Execute; override;
public
constructor Create;
end;
...
TMyThread.Create;
begin
inherited Create (True);
...
Start;
end;
不幸的是,我得到一个例外无法调用运行或被拒绝的线程启动...似乎考虑到文档告诉我,我应该以暂停模式创建的线程调用开始事实,对我来说很奇怪。
Unfortunately I get an exception "Cannot call start on a running or supsended thread"...which seems weird to me considering the fact that the documentation tells me that I should call Start on a thread created in suspended mode.
我在这里缺少什么?
推荐答案
。
The reason is that a Thread is not supposed to start itself.
线程从不知道什么时候初始化做完了。构造与初始化不同(构造应该始终是短暂的,无异常的;进一步的初始化在构建之后完成)。
The thread never knows when initialization is complete. Construction is not the same as initialization (construction should always be short and exception free; further initialization is done after construction).
类似的情况是一个 TDataSet :否 TDataSet 构造函数应该调用打开,或设置活动:= True 。
A similar situation is a TDataSet: no TDataSet constructor should ever call Open, or set Active := True.
另见这个。
您应该:
- 通过调用Create(true)创建TMyThread,并在TMyThread类之外执行Start
- 创建TMyThread非暂挂,确保Create构造函数完全初始化,并让启动主题。
- Create the TMyThread suspended by calling Create(true) and perform the Start outside your TMyThread class
- Create the TMyThread non-suspeneded, making sure the Create constructor does full initialization, and let TThread.AfterConstruction start the thread.
TThread使用说明:
基本上,一个线程应该是这样的:上下文的封装执行代码。
Basically, a thread should be just that: the encapsulation of the context on which code is executed.
执行的实际代码(业务逻辑)应该在其他类中。
The actual code (the business logic) that is executed should then be in other classes.
通过解耦这两个,您可以获得很大的灵活性,特别是在多个地方启动业务逻辑(在单元测试时非常方便)。
By decoupling those two, you gain a lot of flexibility, especially initiating your business logic from within multiple places (which is very convenient when writing unit tests!).
这是您可以使用的框架类型:
This is the kind of framework you could use for that:
unit DecoupledThreadUnit;
interface
uses
Classes;
type
TDecoupledThread = class(TThread)
strict protected
//1 called in the context of the thread
procedure DoExecute; virtual;
//1 Called in the context of the creating thread (before context of the new thread actualy lives)
procedure DoSetUp; virtual;
//1 called in the context of the thread right after OnTerminate, but before the thread actually dies
procedure DoTearDown; virtual;
protected
procedure DoTerminate; override;
procedure Execute; override;
public
constructor Create;
procedure AfterConstruction; override;
end;
implementation
constructor TDecoupledThread.Create;
begin
// create suspended, so that AfterConstruction can call DoSetup();
inherited Create(True);
end;
procedure TDecoupledThread.AfterConstruction;
begin
// DoSetUp() needs to be called without the new thread in suspended state
DoSetUp();
// this will unsuspend the underlying thread
inherited AfterConstruction;
end;
procedure TDecoupledThread.DoExecute;
begin
end;
procedure TDecoupledThread.DoSetUp;
begin
end;
procedure TDecoupledThread.DoTearDown;
begin
end;
procedure TDecoupledThread.DoTerminate;
begin
inherited DoTerminate();
// call DoTearDown on in the thread context right before it dies:
DoTearDown();
end;
procedure TDecoupledThread.Execute;
begin
// call DoExecute on in the thread context
DoExecute();
end;
end.
您甚至可以根据这样做做事件:
You could even make it event based by something like this:
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;
// in case you want to use RTTI
TEventedThread = class(TCustomEventedThread)
published
property OnExecute;
property OnSetUp;
property OnTearDown;
end;
implementation
{ TCustomEventedThread }
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后代,如下所示:
Or adapt it for DUnit TTestCase descendants like this:
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
// this will call SetUp and TearDown on the TestCase
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
--jeroen
这篇关于在Delphi 2010中恢复挂断线程?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!