Einzelnen Beitrag anzeigen

Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#1

CriticalSection und Threads

  Alt 26. Nov 2011, 01:36
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.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat