Возобновление приостановленного потока в Delphi 2010?

StackOverflow https://stackoverflow.com/questions/1624071

Вопрос

Метод возобновления TThread устарел в D2010.Итак, я подумал, что теперь это должно работать следующим образом:

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

TMyThread.Create;
begin
  inherited Create (True);
  ...
  Start;
 end;

К сожалению, я получаю исключение "Не удается вызвать start в запущенном или поддерживаемом потоке" ... что кажется мне странным, учитывая тот факт, что в документации говорится, что я должен вызвать Start в потоке, созданном в приостановленном режиме.

Чего я здесь не понимаю?

Это было полезно?

Решение

Причина в том, что Нитки не должен запускаться сам по себе.

Поток никогда не знает, когда инициализация завершена.Построение - это не то же самое, что инициализация (построение всегда должно быть коротким и без исключений;дальнейшая инициализация выполняется после построения).

Аналогичная ситуация наблюдается в TDataSet - набор данных:НЕТ TDataSet - набор данных конструктор должен когда-либо вызывать Открыть, или установить Активный := True.

Смотрите также это запись в блоге Wings of Wind.

Вам следует либо:

  • Создайте приостановленный TMyThread, вызвав Create(true) и выполните запуск вне вашего класса TMyThread
  • Создайте TMyThread без приостановки, убедившись, что конструктор Create выполняет полную инициализацию, и позвольте TThread.После завершения строительства запустите поток.

Объяснение использования 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.

Вы даже могли бы сделать это на основе событий, используя что-то вроде этого:

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 следующим образом:

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.

--джерун

Другие советы

Краткий ответ: вызовите унаследованный Create (false) и пропустите Start!

Фактический запуск не созданного приостановленным потоком выполняется в AfterConstruction, которая вызывается после вызова всех конструкторов.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top