The reason is that a Thread is not supposed to start itself.
El subproceso nunca se sabe cuando se completa la inicialización. La construcción no es lo mismo que la inicialización (la construcción siempre debe ser corta y sin excepciones, la posterior inicialización se realiza después de la construcción).
Una situación similar es un TDataSet : no TDataSet constructor siempre debe llamar abierto, o conjunto activo: = True.
Ver también este artículo blog entry by Wings of Wind.
Usted deberá:
- Crear el TMyThread suspendida llamando Crear (verdadero) y efectuar la puesta fuera de su clase TMyThread
- Crear la no suspeneded, asegurándose de que el Crear constructor hace inicialización completa TMyThread y deje que TThread.AfterConstruction inicie el hilo.
Explicación del uso TThread:
Básicamente, un hilo debe ser sólo eso: la encapsulación del contexto en el que se ejecuta el código.
El código real (la lógica de negocio) que se ejecuta debe estar en otras clases.
Al desacoplar esos dos, obtiene mucha flexibilidad, especialmente al iniciar su lógica de negocios desde varios lugares (¡lo cual es muy conveniente al escribir pruebas de unidad!).
Este es el tipo de marco que podría utilizar para ello:
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.
Incluso podría hacerlo evento basado en algo como esto:
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 adaptarlo para los descendientes DUnit TTestCase como esto:
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
la forma correcta sería 'MyThread: = TMyThread.Create' y luego' MyThread.Start', realizando todas las inicializaciones largas al comienzo de 'Execute'? ¿Cómo se supone que los usuarios de la clase saben que el hilo debe iniciarse manualmente? (Documentación aparte) – jpfollenius
Como se supone que los usuarios de la clase están completamente versados en la programación multiproceso, eso debería ser un detalle menor. No permita que una clase de subprocesos múltiples se escape a personas que no están preparadas para manejar todos los matices de la programación multiproceso. –
En realidad, ver mi edición: hay dos formas de hacerlo. En general, un hilo no debería saber sobre su inicialización, ya que eso depende de factores fuera del alcance de ese hilo. Sé que la mayoría de las clases de subprocesos son específicas de un problema. Pero deberían dividirse en dos: la clase de subprocesos en sí que solo ejecuta 'código', y una clase de negocios que sabe qué código debe ejecutarse y en qué orden (inicialización, bloque principal, finalización). –