AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

CriticalSections wie verwenden?

Ein Thema von Bummi · begonnen am 28. Nov 2010 · letzter Beitrag vom 21. Mai 2017
Antwort Antwort
Seite 1 von 5  1 23     Letzte » 
Benutzerbild von Bummi
Bummi

Registriert seit: 15. Jun 2010
Ort: Augsburg Bayern Süddeutschland
3.470 Beiträge
 
Delphi XE3 Enterprise
 
#1

CriticalSections wie verwenden?

  Alt 28. Nov 2010, 01:03
Umzug der CriticalSection Diskusion aus
verhindern: Klick auf Titelleiste stoppt Programm #27

http://www.delphipraxis.net/156304-v...ml#post1064523

@Sir Rufo
Zitat:
Aus diesem Grund hänge ich die Instanz von TCriticalSection auch direkt an den Thread, um dessen Properties so zu schützen, dass kein gleichzeitiger Zugriff darauf erfolgen kann. Mittels Getter und Setter wird dann automatisch die CS betreten/verlassen. Somit wird der Zugriff auf selbige extrem stressfrei.

Mir ist nicht ganz klar was Du damit schützt, die Bereiche innerhalb Deiner Klasse die nicht von anderen Threads erreicht werden können brauchen IMHO nicht mit einer CriticalSection gekapselt werden.

Wenn Du einen Bereich in eine CriticalSection packst mußt Du Dich so zumindest mein Kenntnissstand darauf verlassen können daß andere über die gleich CriticalSection darauf zugreifen.

Hast Du Punkt #26 aus o.g. Thread einmal nachvollzugen? Die Variable txt ist nur sicher zugreifbar wenn sich alle daran halten vor dem Zugriff ein und dieselbe CriticalSection zu verwenden.
Thomas Wassermann H₂♂
Das Problem steckt meistens zwischen den Ohren
DRY DRY KISS
H₂ (wenn bei meinen Snipplets nichts anderes angegeben ist Lizenz: WTFPL)
  Mit Zitat antworten Zitat
Benutzerbild von Assarbad
Assarbad

Registriert seit: 8. Okt 2010
Ort: Frankfurt am Main
1.234 Beiträge
 
#2

AW: CriticalSections wie verwenden?

  Alt 28. Nov 2010, 01:22
Wenn Du einen Bereich in eine CriticalSection packst mußt Du Dich so zumindest mein Kenntnissstand darauf verlassen können daß andere über die gleich CriticalSection darauf zugreifen.
Absolut korrekt. Nur wenn sich alle daran halten, hilft der kritische Abschnitt.

Die Variable txt ist nur sicher zugreifbar wenn sich alle daran halten vor dem Zugriff ein und dieselbe CriticalSection zu verwenden.
Jupp.

Ich nehme allerdings an, daß hier nur die Ausdrucksweise unglücklich war. Wenn der Getter und Setter (oder ggf. nur Setter, je nach Datentyp) den kritischen Abschnitt benutzen, ist das ja nicht auf einen Thread beschränkt, wie die Aussage erstmal vermuten läßt.
Oliver
"... aber vertrauen Sie uns, die Physik stimmt." (Prof. Harald Lesch)
  Mit Zitat antworten Zitat
Benutzerbild von Bummi
Bummi

Registriert seit: 15. Jun 2010
Ort: Augsburg Bayern Süddeutschland
3.470 Beiträge
 
Delphi XE3 Enterprise
 
#3

AW: CriticalSections wie verwenden?

  Alt 28. Nov 2010, 01:30
Zitat:
Ich nehme allerdings an, daß hier nur die Ausdrucksweise unglücklich war. Wenn der Getter und Setter (oder ggf. nur Setter, je nach Datentyp) den kritischen Abschnitt benutzen, ist das ja nicht auf einen Thread beschränkt, wie die Aussage erstmal vermuten läßt.
hier hakt es bei mir, wofür brauche ich hier dann lokale (innerhalb des Threads gültige) CriticalSections.
Thomas Wassermann H₂♂
Das Problem steckt meistens zwischen den Ohren
DRY DRY KISS
H₂ (wenn bei meinen Snipplets nichts anderes angegeben ist Lizenz: WTFPL)
  Mit Zitat antworten Zitat
Benutzerbild von Assarbad
Assarbad

Registriert seit: 8. Okt 2010
Ort: Frankfurt am Main
1.234 Beiträge
 
#4

AW: CriticalSections wie verwenden?

  Alt 28. Nov 2010, 01:48
hier hakt es bei mir, wofür brauche ich hier dann lokale (innerhalb des Threads gültige) CriticalSections.
Jupp, tut mir leid. Hatte das Originalthema nicht komplett gelesen und auch jetzt nur überflogen. Aber der Code bei dem jeder Thread seine eigene CS hat, ist natürlich Unsinn. Ein Synchronisationsobjekt funktioniert nur wenn alle das gleiche benutzen. Oder um Luckie zu zitieren:

Wenn mehrere Threads aufeinander warten müssen, dann dürfen sie auch nur die gleiche CriticalSection nutzen. Das ist wie bei einer Ampelkreuzung, die darf auch nur von einer Schaltung gesteuert werden. Hätte jede Ampel ihre eigene Steuerung würde es krachen.
Oliver
"... aber vertrauen Sie uns, die Physik stimmt." (Prof. Harald Lesch)
  Mit Zitat antworten Zitat
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, 01: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
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.288 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: CriticalSections wie verwenden?

  Alt 28. Nov 2010, 02:05
hier hakt es bei mir, wofür brauche ich hier dann lokale (innerhalb des Threads gültige) CriticalSections.
Wenn es um den Zugriff auf Resourcen des Threads geht (wie in dem Beispiel, um das es geht), dann reicht auch eine threadlokale kritische Sektion. Denn da im Getter und Setter diese genauso benutzt wird wie innerhalb des Threads, ist kein paralleler Zugriff auf das private Feld hinter Getter bzw. Setter möglich.

Der Zugriff aus einem anderen Thread passiert ja über die Getter und Setter bzw. die entsprechende Property und damit wird der Zugriff auch effektiv geschützt. Und dabei muss dennoch nicht mit dem Hauptthread synchronisiert werden, sondern es wird nur die kritische Sektion benutzt.

Nicht funktionieren tut es, wenn der Zugriff auf eine Resource eben nicht immer über die selbe kritische Sektion erfolgt. Aber das ist hier nicht der Fall, da jeder Thread zwar eine eigene kritische Sektion, aber auch eine eigene zu schützende Resource hat.
Sebastian Jänicke
Alle eigenen Projekte sind eingestellt, ebenso meine Homepage, Downloadlinks usw. im Forum bleiben aktiv!
  Mit Zitat antworten Zitat
Benutzerbild von Bummi
Bummi

Registriert seit: 15. Jun 2010
Ort: Augsburg Bayern Süddeutschland
3.470 Beiträge
 
Delphi XE3 Enterprise
 
#7

AW: CriticalSections wie verwenden?

  Alt 28. Nov 2010, 02:10
Danke , aber wenn jeder Thread seien eigen CriticalSection erzeugt wie ist dann ein übergreifender Zugriff gewährleistet, oder da dies ja nicht eigentlich das Problem hier ist, wofür benötige ich die Threadinternen CS überhaupt?
Thomas Wassermann H₂♂
Das Problem steckt meistens zwischen den Ohren
DRY DRY KISS
H₂ (wenn bei meinen Snipplets nichts anderes angegeben ist Lizenz: WTFPL)
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

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

AW: CriticalSections wie verwenden?

  Alt 28. Nov 2010, 02:12
hier hakt es bei mir, wofür brauche ich hier dann lokale (innerhalb des Threads gültige) CriticalSections.
Jupp, tut mir leid. Hatte das Originalthema nicht komplett gelesen und auch jetzt nur überflogen. Aber der Code bei dem jeder Thread seine eigene CS hat, ist natürlich Unsinn. Ein Synchronisationsobjekt funktioniert nur wenn alle das gleiche benutzen.
Hmmm, das sehe ich anders, wenn ich optimal laufenden Code erzeugen will mit der maximal möglichen Performance. Natürlich wird es auch mit einer externen CS funktionieren, aber eben nicht so performant wie es laufen könnte.

Beispiel:
Delphi-Quellcode:
GlobalCS : TCriticalSection;

TMyThread = class( TThread )
{...}
  Info : Int64;
end;

procedure TMyThread.Execute;
  begin
    while not Terminated do
      begin
        GlobalCS.Enter;
        try
          QueryPerformanceCounter( Info );
        finally
          GlobalCS.Leave;
        end;
      end;
  end;

MyThread1, MyThread2 : TMyThread;

procedure ImMainThread;
var
  ThInfo : Int64;
begin
  GlobalCS.Enter;
  // Beide Threads MyThread1, MyThread2 stoppen jetzt, weil sie ja
  // GlobalCS nicht mehr betreten können.
  // Ich will aber nur von MyThread1 etwas wissen, warum soll MyThread2 dann gestört werden???
  try
    ThInfo := MyThread1.Info;
  finally
    GlobalCS.Leave;
  end;
end;
Meine Variante sieht so aus
Delphi-Quellcode:

TMyThread = class( TThread )
  ThreadCS : TCriticalSection;
  FInfo : int64;
{...}
  property Info : Int64 read GetInfo;
end;

function GetInfo : Int64;
  begin
    ThreadCS.Enter;
    try
      Result := FInfo;
    finally
      ThreadCS.Leave;
    end;
  end;

procedure TMyThread.Execute;
  begin
    while not Terminated do
      begin
        ThreadCS.Enter;
        try
          QueryPerformanceCounter( FInfo );
        finally
          ThreadCS.Leave;
        end;
      end;
  end;

MyThread1, MyThread2 : TMyThread;

procedure ImMainThread;
var
  ThInfo : Int64;
begin
  ThInfo := MyThread1.Info;
end;
Der Zugriff auf die Property ist wesentlich leichter und es wird nur der Thread gestört, den ich abfrage.
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)

Geändert von Sir Rufo (28. Nov 2010 um 02:23 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

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

AW: CriticalSections wie verwenden?

  Alt 28. Nov 2010, 02:16
Danke , aber wenn jeder Thread seien eigen CriticalSection erzeugt wie ist dann ein übergreifender Zugriff gewährleistet, oder da dies ja nicht eigentlich das Problem hier ist, wofür benötige ich die Threadinternen CS überhaupt?
a. Performance
b. der Thread selber ist per Definition auch threadsafe für Zugriffe von außen (über die Properties)

meine Threads übernehme ich in jedes beliebige Projekt und benutze diese ohne mich um irgendeine CS weiter zu kümmern, dann das regelt der Thread ja in sich selber.

Greift Thread A auf eine Property aus Thread B zu, dann ist die Property von Thread B ja geschützt. Somit kann es einfach keine Zugriffsfehler geben, da diese immer geschützt sind.

Ist quasi die Merz-Spezial-Dragee-Methode (Wahre Schönheit kommt von innen)
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
Benutzerbild von Bummi
Bummi

Registriert seit: 15. Jun 2010
Ort: Augsburg Bayern Süddeutschland
3.470 Beiträge
 
Delphi XE3 Enterprise
 
#10

AW: CriticalSections wie verwenden?

  Alt 28. Nov 2010, 02:21
@Sir Rufo
Das eben macht mir Kopfschmerzen #9 b
Wenn Du für jede Instanz eine eigene CS erzeugst wie soll der Zugriff dann geschützt sein.

Aber ich muß jetzt ins Bett, gerne morgen weiter
Thomas Wassermann H₂♂
Das Problem steckt meistens zwischen den Ohren
DRY DRY KISS
H₂ (wenn bei meinen Snipplets nichts anderes angegeben ist Lizenz: WTFPL)
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

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 23:35 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz