Einzelnen Beitrag anzeigen

Benutzerbild von Sir Rufo
Sir Rufo

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

AW: CriticalSections wie verwenden?

  Alt 28. Nov 2010, 00:57
Um das mit den CriticalSections mal etwas besser zu verdeutlichen habe ich mal mein WorkThread-Beispiel etwas umgeändert um folgende Situationen darzustellen:
- optimales Laufzeitverhalten mit einem Minimum an Störungen des Threads durch Zugriffe von außen (bzw. durch den Event, der ja in einem anderen Thread-Kontext läuft und somit auch einen Zugriff von außen darstellt)
- blockierndes Laufzeitverhalten durch ein ungeschicktes Verwenden von CS
- Deadlock durch CS

Mit den beiden Compilerschaltern kann der Thread entsprechend eingestellt werden.
Delphi-Quellcode:
unit uWorkThread;

{.$DEFINE DEADLOCK}
{.$DEFINE BLOCKING}

interface

uses
  Generics.Collections,
  Classes,
  SyncObjs;

type
  TWorkItem = record
    TimeStamp : TDateTime;
  end;

  TWorkQueue = TQueue< TWorkItem >;

  TWorkNotify = procedure( WorkItem : TWorkItem ) of object;
  TWorkType = ( wtSync, wtQueue );

  TWorkThread = class( TThread )
  private
    { Private-Deklarationen }

    FCSWork : TCriticalSection;
    FCSQueue : TCriticalSection;
    FCSWorkType : TCriticalSection;

    FWorkQueue : TWorkQueue;
    FOnWork : TWorkNotify;
    FWorkType : TWorkType;

    procedure DoOnWork;
    procedure SetOnWork( const Value : TWorkNotify );
    function GetOnWork : TWorkNotify;
    procedure SetWorkType( const Value : TWorkType );
    function GetWorkType : TWorkType;

  protected
    procedure Execute; override;

  public
    property WorkType : TWorkType read GetWorkType write SetWorkType;
    property OnWork : TWorkNotify read GetOnWork write SetOnWork;
    constructor Create( CreateSuspended : Boolean );
    destructor Destroy; override;
  end;

implementation

uses
  SysUtils;

{ TProgressThread }

constructor TWorkThread.Create( CreateSuspended : Boolean );
  begin
    FCSWork := TCriticalSection.Create;
    FCSQueue := TCriticalSection.Create;
    FCSWorkType := TCriticalSection.Create;

    FCSWork.Enter;
    FCSQueue.Enter;
    FCSWorkType.Enter;
    try
      inherited;

      FWorkQueue := TWorkQueue.Create;
      FWorkType := wtSync;

    finally
      FCSWork.Leave;
      FCSQueue.Leave;
      FCSWorkType.Leave;
    end;
  end;

destructor TWorkThread.Destroy;
  begin
    FCSWork.Enter;
    FCSQueue.Enter;
    FCSWorkType.Enter;
    try

      FWorkQueue.Free;

      inherited;
    finally
      FCSWork.Leave;
      FCSQueue.Leave;
      FCSWorkType.Leave;
      FreeAndNil( FCSWork );
      FreeAndNil( FCSQueue );
      FreeAndNil( FCSWorkType );
    end;
  end;

procedure TWorkThread.DoOnWork;
  var
    WorkItem : TWorkItem;
  begin

    FCSWork.Enter;
    try
      WorkItem := FWorkQueue.Dequeue;
    finally
      FCSWork.Leave;
    end;

    {$IFDEF BLOCKING}

    FCSWork.Enter;

    {$ELSE}

    FCSQueue.Enter;

    {$ENDIF}

    try

      if Assigned( FOnWork ) then
        FOnWork( WorkItem );

    finally

      {$IFDEF BLOCKING}

      FCSWork.Leave;

      {$ELSE}

      FCSQueue.Leave;

      {$ENDIF}

    end;

  end;

procedure TWorkThread.Execute;
  var
    WorkItem : TWorkItem;
    Counter : Integer;
  begin
    { Thread-Code hier einfügen }
    Counter := 0;
    while not Terminated and ( Counter < 1000 ) do
      begin

        WorkItem.TimeStamp := Now;
        Inc( Counter );

        FCSWork.Enter;
        try
          FWorkQueue.Enqueue( WorkItem );
        finally
          FCSWork.Leave;
        end;

        {$IFDEF DEADLOCK}

        FCSWork.Enter;
        try

          {$ENDIF}

          case WorkType of
            wtSync :
              Synchronize( DoOnWork );
            wtQueue :
              Queue( DoOnWork );
          end;

          {$IFDEF DEADLOCK}

        finally
          FCSWork.Leave;
        end;

        {$ENDIF}

        //Sleep( 10 );
      end;
  end;

function TWorkThread.GetOnWork : TWorkNotify;
  begin

    {$IFDEF BLOCKING}

    FCSWork.Enter;

    {$ELSE}

    FCSQueue.Enter;

    {$ENDIF}

    try

      Result := FOnWork;

    finally

      {$IFDEF BLOCKING}

      FCSWork.Leave;

      {$ELSE}

      FCSQueue.Leave;

      {$ENDIF}

    end;
  end;

function TWorkThread.GetWorkType : TWorkType;
  begin
    FCSWorkType.Enter;
    try
      Result := FWorkType;
    finally
      FCSWorkType.Leave;
    end;
  end;

procedure TWorkThread.SetOnWork( const Value : TWorkNotify );
  begin

    {$IFDEF BLOCKING}

    FCSWork.Enter;

    {$ELSE}

    FCSQueue.Enter;

    {$ENDIF}

    try

      FOnWork := Value;

    finally

      {$IFDEF BLOCKING}

      FCSWork.Leave;

      {$ELSE}

      FCSQueue.Leave;

      {$ENDIF}

    end;
  end;

procedure TWorkThread.SetWorkType( const Value : TWorkType );
  begin
    FCSWorkType.Enter;
    try
      FWorkType := Value;
    finally
      FCSWorkType.Leave;
    end;
  end;

end.
Wenn man jetzt die Laufzeiten von optimal und blockierend betrachtet (deadlock läuft ja gar nicht) dann sieht man folgendes:
Erstellung: Zeitpunkt der Erstellung im Thread
Ausgabe: Zeitpunkt der Ausgabe in der ListBox
optimal
Code:
Erstellung    Ausgabe
============  ============
01:43:52.380 - 01:43:52.381
01:43:52.380 - 01:43:52.382
01:43:52.380 - 01:43:52.384
01:43:52.380 - 01:43:52.392
01:43:52.380 - 01:43:52.394
01:43:52.380 - 01:43:52.395
01:43:52.380 - 01:43:52.397
01:43:52.380 - 01:43:52.397
01:43:52.380 - 01:43:52.399
...
01:43:52.386 - 01:43:53.961
Der Thread hat nach 6/1000 Sekunden alle 1000 Einträge erzeugt.
Bis zu diesem Zeitpunkt wurden 3 Einträge in die ListBox eingetragen.
Erzeugung und Ausgabe laufen aber parallel, die Ausgabe dauert halt nur länger als die Erstellung

blockierend

Code:
Erstellung    Ausgabe
============  ============
01:42:10.625 - 01:42:10.626 <- Ausgabe erfolgt
01:42:10.625 - 01:42:10.626
...
01:42:10.626 - 01:42:10.671
01:42:10.626 - 01:42:10.673
01:42:10.626 - 01:42:10.675 <- Erstellung stoppt
01:42:10.673 - 01:42:10.677 <- Erstellung geht weiter
01:42:10.673 - 01:42:10.679
01:42:10.673 - 01:42:10.681
...
hier erfolgt nun entweder die Ausgabe oder die Erstellung, aber keine parallele Verarbeitung.

Source und Exe im Anhang
Angehängte Dateien
Dateityp: zip WorkThread.zip (1,11 MB, 38x aufgerufen)
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