在Delphi 2010中恢复挂起的线程?

jpf*_*ius 16 delphi multithreading delphi-2010

在D2010中不推荐使用TThread的简历方法.所以,我认为现在应该这样工作:

TMyThread = class (TThread)
protected
  Execute; override;
public
  constructor Create;
end;
...

TMyThread.Create;
begin
  inherited Create (True);
  ...
  Start;
 end;
Run Code Online (Sandbox Code Playgroud)

不幸的是,我得到一个例外"无法在正在运行或已经上线的线程上调用start"...考虑到文档告诉我应该在以挂起模式创建的线程上调用Start这一事实,这对我来说似乎很奇怪.

我在这里错过了什么?

Jer*_*ers 18

原因是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
    //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.
Run Code Online (Sandbox Code Playgroud)

您甚至可以通过以下方式使其成为事件:

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.
Run Code Online (Sandbox Code Playgroud)

或者适应这样的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
    // 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.
Run Code Online (Sandbox Code Playgroud)

--jeroen

  • 该新闻组线程中有264条消息.你能否请**总结一下**?当然,线程应该知道它的初始化,就像任何其他类应该知道它自己的初始化一样.这就是构造函数的用途.在构造函数完成运行时,该类应该完全可以使用了.引发异常的构造函数没有错; 该语言是*为此而设计的*.一般来说,线程启动本身绝对没有错.唯一的危险是在早期版本中,它很容易解决.(Uwe的回答和我的评论.) (2认同)

Uwe*_*abe 14

简答:调用继承Create(false)并省略Start!

非创建挂起线程的实际开始在AfterConstruction中完成,AfterConstruction在调用所有构造函数后调用.

  • 请注意,这是一个相对较新的发展.一旦继承的构造函数完成运行,旧版本就会开始运行.但是,有一个非常简单的解决方法:最后调用继承的构造函数**.(没有理由先调用它;后代很少需要基本TThread构造函数设置的任何属性值.) (2认同)