Aus aktuellem Anlass und weil mit das Handling der CriticalSections gerade beim Vererben von Threads etwas genervt hat hier mal ein smarter Ansatz:
Wenn in einem Thread eine CriticalSection benötigt wird (Schutz der Datenzugriffe durch unterschiedliche Threads), dann sollte die CriticalSection folgendermassen erzeugt werden:
Delphi-Quellcode:
unit uThreadA;
interface
uses
Classes, SyncObjs;
type
TMyThreadA =
class( TThread )
strict private
_CS : TCriticalSection;
protected
function CS : TCriticalSection;
public
constructor Create( CreateSuspended : Boolean );
destructor Destroy;
override;
end;
implementation
{ TMyThreadA }
function TMyThreadA.CS : TCriticalSection;
begin
if not Assigned( _CS )
then
_CS := TCriticalSection.Create;
Result := _CS;
end;
constructor TMyThreadA.Create( CreateSuspended : Boolean );
begin
CS.Enter;
try
inherited;
finally
CS.Leave;
end;
end;
destructor TMyThreadA.Destroy;
begin
CS.Enter;
try
inherited;
finally
CS.Leave;
// Eigentlich FreeAndNil( _CS ) aber wenn man sich die SysUtils sparen kann ;o)
_CS.Free;
_CS :=
nil;
end;
end;
end.
Die CriticalSection wird noch
vor der eigentlichen Objekt-Instanz erzeugt und schützt auch schon das Erzeugen dieser Instanz.
Die CriticalSection wird
nach der Freigabe der Objekt-Instanz freigegeben und die Freigabe der Objekt-Instanz wird von der CriticalSection noch geschützt.
So weit, so gut ...
Aber was passiert, wenn man diese Thread-Klasse vererben möchte?
Delphi-Quellcode:
unit uThreadB;
interface
uses
Classes, SyncObjs, uThreadA;
type
TMyThreadB =
class( TMyThreadA )
public
constructor Create( CreateSuspended : Boolean );
destructor Destroy;
override;
end;
implementation
{ TMyThreadB }
constructor TMyThreadB.Create( CreateSuspended : Boolean );
begin
CS.Enter;
try
inherited;
finally
CS.Leave;
end;
end;
destructor TMyThreadB.Destroy;
begin
CS.Enter;
try
// inherited
// darf hier nicht stehen, denn sonst wird ja die CS freigegeben
// und im finally-Teil gibt es eine Exception
finally
CS.Leave;
end;
inherited;
end;
end.
Eine Alternative wäre, in jedem Thread (auch den abgeleiteten) eine neue CS einzuführen.
Warum kann die CS denn kein Interface sein? ;o)
Dann könnte man das sehr hübsch so machen:
Delphi-Quellcode:
unit uThreadA;
interface
uses
Classes, SyncObjs;
type
TMyThreadA =
class( TCSThread )
// eine neue Basisklasse Thread mit CS
public
constructor Create( CreateSuspended : Boolean );
destructor Destroy;
override;
end;
implementation
{ TMyThreadA }
constructor TMyThreadA.Create( CreateSuspended : Boolean );
begin
CS.Enter;
try
inherited;
finally
CS.Leave;
end;
end;
destructor TMyThreadA.Destroy;
begin
CS.Enter;
try
inherited;
finally
CS.Leave;
end;
end;
end.
und der davon abgeleitete Thread wird auf die gleiche Weise implementiert, denn nun verflüchtigt sich die CriticalSection dann, wenn diese nicht mehr benötigt wird.
Delphi-Quellcode:
unit uThreadB;
interface
uses
Classes, SyncObjs, uThreadA;
type
TMyThreadB =
class( TMyThreadA )
public
constructor Create( CreateSuspended : Boolean );
destructor Destroy;
override;
end;
implementation
{ TMyThreadB }
constructor TMyThreadB.Create( CreateSuspended : Boolean );
begin
CS.Enter;
try
inherited;
finally
CS.Leave;
end;
end;
destructor TMyThreadB.Destroy;
begin
CS.Enter;
try
inherited
finally
CS.Leave;
end;
end;
end.
Und hier das Interface mit der Thread-Klasse:
Delphi-Quellcode:
unit uCSObjects;
interface
uses
Classes,
SyncObjs;
type
ICriticalSection =
interface
['
{DE0BF9E0-92C0-424B-A70F-5C58CD412C1A}']
procedure Enter;
function TryEnter : Boolean;
procedure Leave;
end;
TInterfacedCriticalSection =
class( TInterfacedObject, ICriticalSection )
strict private
_CS : TCriticalSection;
protected
procedure Enter;
function TryEnter : Boolean;
procedure Leave;
public
constructor Create;
destructor Destroy;
override;
end;
TCSThread =
class( TThread )
strict private
_CS : ICriticalSection;
protected
function CS : ICriticalSection;
end;
implementation
{ TInterfacedCriticalSection }
constructor TInterfacedCriticalSection.Create;
begin
inherited;
_CS := TCriticalSection.Create;
end;
destructor TInterfacedCriticalSection.Destroy;
begin
_CS.Free;
_CS :=
nil;
inherited;
end;
procedure TInterfacedCriticalSection.Enter;
begin
_CS.Enter;
end;
procedure TInterfacedCriticalSection.Leave;
begin
_CS.Leave;
end;
function TInterfacedCriticalSection.TryEnter : Boolean;
begin
Result := _CS.TryEnter;
end;
{ TCSThread }
function TCSThread.CS : ICriticalSection;
begin
if not Assigned( _CS )
then
_CS := TInterfacedCriticalSection.Create;
Result := _CS;
end;
end.