Riprendi il thread sospeso in Delphi 2010?
-
06-07-2019 - |
Domanda
Il metodo di ripristino di TThread è obsoleto in D2010. Quindi, ho pensato che ora dovrebbe funzionare così:
TMyThread = class (TThread)
protected
Execute; override;
public
constructor Create;
end;
...
TMyThread.Create;
begin
inherited Create (True);
...
Start;
end;
Sfortunatamente ricevo un'eccezione "Impossibile chiamare start su un thread in esecuzione o sospeso" ... che mi sembra strano considerando il fatto che la documentazione mi dice che dovrei chiamare Start su un thread creato in modalità sospesa.
Cosa mi sto perdendo qui?
Soluzione
Il motivo è che una discussione è non dovrebbe iniziare se stesso .
Il thread non sa mai quando l'inizializzazione è completa. La costruzione non è la stessa dell'inizializzazione (la costruzione deve essere sempre breve e senza eccezioni; l'ulteriore inizializzazione viene eseguita dopo la costruzione).
Una situazione simile è un TDataSet : nessun costruttore TDataSet dovrebbe mai chiamare Apri o impostare Attivo: = Vero .
Vedi anche questo post nel blog di Wings of Wind .
Dovresti:
- Crea il TMyThread sospeso chiamando Create (true) ed esegui Start al di fuori della tua classe TMyThread
- Crea il TMyThread senza sospensioni, assicurandoti che il costruttore Crea esegua l'inizializzazione completa e che TThread.AfterConstruction avvia la discussione.
Spiegazione dell'utilizzo di TThread :
Fondamentalmente, un thread dovrebbe essere proprio questo: l'incapsulamento del contesto su cui viene eseguito il codice.
Il codice effettivo (la logica aziendale) che viene eseguito dovrebbe quindi essere in altre classi.
Disaccoppiando questi due, si ottiene molta flessibilità, in particolare avviando la propria logica aziendale da più punti (il che è molto conveniente quando si scrivono test unitari!).
Questo è il tipo di framework che potresti usare per questo:
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.
Potresti persino renderlo un evento basato su qualcosa del genere:
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.
O adattalo per i discendenti di DUnit TTestCase in questo modo:
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
Altri suggerimenti
Risposta breve: chiama ereditato Crea (falso) e ometti Start!
L'inizio effettivo di un thread non creato-sospeso viene eseguito in AfterConstruction, che viene chiamato dopo che tutti i costruttori sono stati chiamati.