AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

CriticalSections wie verwenden?

Ein Thema von Bummi · begonnen am 28. Nov 2010 · letzter Beitrag vom 21. Mai 2017
Antwort Antwort
Seite 2 von 2     12   
Benutzerbild von Sir Rufo
Sir Rufo

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

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
Antwort Antwort
Seite 2 von 2     12   


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 20:05 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz