![]() |
CriticalSections wie verwenden?
Umzug der CriticalSection Diskusion aus
verhindern: Klick auf Titelleiste stoppt Programm #27 ![]() @Sir Rufo Zitat:
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. |
AW: CriticalSections wie verwenden?
Zitat:
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. |
AW: CriticalSections wie verwenden?
Zitat:
|
AW: CriticalSections wie verwenden?
Zitat:
Zitat:
|
AW: CriticalSections wie verwenden?
Liste der Anhänge anzeigen (Anzahl: 1)
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:
Wenn man jetzt die Laufzeiten von optimal und blockierend betrachtet (deadlock läuft ja gar nicht) dann sieht man folgendes:
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. Erstellung: Zeitpunkt der Erstellung im Thread Ausgabe: Zeitpunkt der Ausgabe in der ListBox optimal
Code:
Der Thread hat nach 6/1000 Sekunden alle 1000 Einträge erzeugt.
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 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:
hier erfolgt nun entweder die Ausgabe oder die Erstellung, aber keine parallele Verarbeitung.
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 ... Source und Exe im Anhang |
AW: CriticalSections wie verwenden?
Zitat:
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. |
AW: CriticalSections wie verwenden?
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?
|
AW: CriticalSections wie verwenden?
Zitat:
Beispiel:
Delphi-Quellcode:
Meine Variante sieht so aus
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;
Delphi-Quellcode:
Der Zugriff auf die Property ist wesentlich leichter und es wird nur der Thread gestört, den ich abfrage.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; |
AW: CriticalSections wie verwenden?
Zitat:
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) :mrgreen: |
AW: CriticalSections wie verwenden?
@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:thumb: |
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:11 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