Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Critical Section um globale Methode? (https://www.delphipraxis.net/192619-critical-section-um-globale-methode.html)

Rabenrecht 5. Mai 2017 09:02

Critical Section um globale Methode?
 
Ich habe eine Methode, die in einer Unit mit global verfügbare Klassen und Methoden liegt.

Die Methode behandelt Clipboard-Operationen. Hierbei kann es zu Konflikten und folgender Fehlermeldung kommen: "Zwischenablage Zugriff verweigert kann nicht geöffnet werden"

Nach meiner Recherche kommt es zu dieser Meldung, wenn zwischen Clipboard.Open und Clipboard.Close ein weiterer Zugriff auf die Zwischenablage erfolgt.

Als Lösung viel mir als erstes ein, dass der Code von Clipboard.Open und Clipboard.Close eigentlich ein kritischer Abschnitt ist. Leider kenne ich mich mit der Implementierung von critical sections unter Delphi nicht so gut aus. Wie ließe sich das in diesem Kontext implementieren? Oder gibt es andere Lösungen für das Problem?

Hier mal etwa Code:

Delphi-Quellcode:
Unit GlobalStuff;
...
function SaveClipboard : TList;
...
function SaveClipboard : TList;
begin
  ...
  ClipBoard.Open;
  ...
  ClipBoard.Close;
  ...
end;
Wäre es zb. möglich eine Unit-globale Variable vom Typ CriticalSection zu deklarieren und diese dann in SaveClipboard zu verwenden?

Der schöne Günther 5. Mai 2017 09:28

AW: Critical Section um globale Methode?
 
Als erstes fiele mir auf jeden Fall ein Das Open/Close erst einmal mit einem
Delphi-Quellcode:
try..finally
abzusichern ;-)

Ein kritischer Abschnitt oder ähnliche Sicherungsmechanismen helfen dir eigentlich nur wenn du genau diese Methode aus verschiedenen Threads gleichzeitig aufrufen willst. Wenn dir da irgendein anderer Mechanismus für den Zwischenablagen-Zugriff reinfunkt (z.B. durch Drittanbieter-Komponenten oder ähnliches) hilft dir das auch nichts.

Rabenrecht 5. Mai 2017 09:52

AW: Critical Section um globale Methode?
 
Das stimmt. Die Vermutung besteht aber, dass verschiedene Threads unseres Programmes auf die Methode zugreifen. Das ist theoretisch möglich, da die Methode wie gesagt global verfügbar ist. Ob das aber wirklich so passiert, kann ich nicht sagen. Ein Kunde hat das Problem berichtet, reproduziert bekomme ich es nicht :wink:

Daher wäre ohnehin jede Lösung, die ich einbaue, ein Schuss ins Blaue.

Der schöne Günther 5. Mai 2017 10:10

AW: Critical Section um globale Methode?
 
Soweit wir noch nicht bei Science Fiction sind sollte dein Programm auch nur das tun was im Quelltext steht. Schau doch einfach an welchen Stellen im Code die Methode aufgerufen wird und nicht "theoretisch könnte das sein" ;-)


Critical Sections sind einfach: Du erzeugst dir einmalig so ein Objekt, und immer um einen Abschnitt zu sperren sagst du einmal
Delphi-Quellcode:
Acquire()
und wenn du fertig bist
Delphi-Quellcode:
Release()
http://docwiki.embarcadero.com/RADSt...che_Abschnitte

Delphi-Quellcode:
uses
   System.SyncObjs;
var
   criticalSection: TSynchroObj;

   
procedure funWithClipboard();
begin
   criticalSection.Acquire();
   try
      ClipBoard.Open();
      try
         // (...)
      finally
         ClipBoard.Close();
      end;
   finally
      criticalSection.Release();
   end;
end;


PS: Software die das Haus verlässt braucht unbedingt vernünftiges Exception-Logging sodass du direkt den kompletten Aufruf-Stack bekommst. Wenn die einzige Info nur ein kurzer Text auf einer Messagebox ist wird man echt nicht glücklich.

Rabenrecht 5. Mai 2017 11:21

AW: Critical Section um globale Methode?
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1370299)
Soweit wir noch nicht bei Science Fiction sind sollte dein Programm auch nur das tun was im Quelltext steht. Schau doch einfach an welchen Stellen im Code die Methode aufgerufen wird und nicht "theoretisch könnte das sein" ;-)

Die Codebasis ist über mehr als 20 Jahre entstanden, das Programm entsprechend groß und teilweise unübersichtlich. So einfach ist das leider nicht.

Zitat:

PS: Software die das Haus verlässt braucht unbedingt vernünftiges Exception-Logging sodass du direkt den kompletten Aufruf-Stack bekommst. Wenn die einzige Info nur ein kurzer Text auf einer Messagebox ist wird man echt nicht glücklich.
Haben wir. Bringt uns hier nur nichts, da ich das Problem bisher nicht reproduzieren kann. Ich muss mit dem Arbeiten, was der Kunde mitteilt.

Zitat:

Critical Sections sind einfach: Du erzeugst dir einmalig so ein Objekt, und immer um einen Abschnitt zu sperren sagst du einmal
Delphi-Quellcode:
Acquire()
und wenn du fertig bist
Delphi-Quellcode:
Release()
http://docwiki.embarcadero.com/RADSt...che_Abschnitte

Delphi-Quellcode:
uses
   System.SyncObjs;
var
   criticalSection: TSynchroObj;

   
procedure funWithClipboard();
begin
   criticalSection.Acquire();
   try
      ClipBoard.Open();
      try
         // (...)
      finally
         ClipBoard.Close();
      end;
   finally
      criticalSection.Release();
   end;
end;

Das werde ich mal versuchen, danke :)

jaenicke 5. Mai 2017 13:42

AW: Critical Section um globale Methode?
 
Viel einfacher:
Delphi-Quellcode:
TMonitor.Enter(ClipBoard);
try
  ...
finally
  TMonitor.Exit(ClipBoard);
end;
Schneller als eine Critical Section, genau auf das Objekt bezogen und ohne zusätzliche Initialisierung usw.

Zacherl 5. Mai 2017 14:26

AW: Critical Section um globale Methode?
 
Zitat:

Zitat von jaenicke (Beitrag 1370323)
Schneller als eine Critical Section

Sofern sich da nichts geändert hat, dann leider nicht. Siehe z.b.:
https://www.delphitools.info/2013/06...iticalsection/

Meines Wissens nach versucht die Windows Implementation der Critical Section mitlerweile auch erstmal ein SpinLock, bevor es dann den teuren Context-Switch in den Kernel gibt. Sollte also nun sogar noch performanter sein.

jaenicke 5. Mai 2017 14:33

AW: Critical Section um globale Methode?
 
In deinem Link steht doch genau die Antwort auf die Berichte über die schlechte Performance. Seit XE5 ist das behoben und schneller als eine CS.
Hier auch nochmal der Link:
https://community.embarcadero.com/bl...-monitor-38952

TiGü 5. Mai 2017 14:35

AW: Critical Section um globale Methode?
 
Zitat:

Zitat von Zacherl (Beitrag 1370332)
Zitat:

Zitat von jaenicke (Beitrag 1370323)
Schneller als eine Critical Section

Sofern sich da nichts geändert hat, dann leider nicht. Siehe z.b.:
https://www.delphitools.info/2013/06...iticalsection/

Laut diesem Blogpost ist das seit XE5 gefixt:
http://blog.synopse.info/post/2016/0...d-applications

freimatz 11. Mai 2017 09:29

AW: Critical Section um globale Methode?
 
Müssen Clipboard Operationen nicht auch im Mainthread ablaufen? Dann wäre noch ein Synchronize nötig und könnte vielleicht auch schon alleine helfen.

mjustin 11. Mai 2017 12:33

AW: Critical Section um globale Methode?
 
Zitat:

Zitat von freimatz (Beitrag 1370995)
Müssen Clipboard Operationen nicht auch im Mainthread ablaufen? Dann wäre noch ein Synchronize nötig und könnte vielleicht auch schon alleine helfen.

Synchronize aus dem Mainthread heraus aufzurufen wird ausdrücklich nicht empfohlen:

Warning: Do not call Synchronize from within the main thread. This can cause an infinite loop.

http://docwiki.embarcadero.com/Libra...ad.Synchronize

TiGü 11. Mai 2017 13:01

AW: Critical Section um globale Methode?
 
Zitat:

Zitat von mjustin (Beitrag 1371015)
Zitat:

Zitat von freimatz (Beitrag 1370995)
Müssen Clipboard Operationen nicht auch im Mainthread ablaufen? Dann wäre noch ein Synchronize nötig und könnte vielleicht auch schon alleine helfen.

Synchronize aus dem Mainthread heraus aufzurufen wird ausdrücklich nicht empfohlen:

Warning: Do not call Synchronize from within the main thread. This can cause an infinite loop.

http://docwiki.embarcadero.com/Libra...ad.Synchronize

Daher prüft man das vorher und ruft nur dann die Variante mit Synchronize auf.

Zacherl 11. Mai 2017 13:48

AW: Critical Section um globale Methode?
 
Zitat:

Zitat von jaenicke (Beitrag 1370333)
In deinem Link steht doch genau die Antwort auf die Berichte über die schlechte Performance. Seit XE5 ist das behoben und schneller als eine CS.
Hier auch nochmal der Link:
https://community.embarcadero.com/bl...-monitor-38952

Zitat:

Zitat von TiGü (Beitrag 1370334)
Laut diesem Blogpost ist das seit XE5 gefixt:
http://blog.synopse.info/post/2016/0...d-applications

Oh, gut zu wissen. Hatte den Artikel noch von "damals" im Kopf und habe natürlich nur ganz unten nach Updates gesucht, bevor ich ihn gepostet habe :wall: :-D Aber gut, dann scheint die Performance von
Delphi-Quellcode:
TMonitor
ja jetzt zumindest gleichwertig zur CriticalSection zu sein, wenn man einen adäquaten Wert für den SpinCount wählt.

jaenicke 11. Mai 2017 21:24

AW: Critical Section um globale Methode?
 
Zitat:

Zitat von Zacherl (Beitrag 1371018)
Aber gut, dann scheint die Performance von
Delphi-Quellcode:
TMonitor
ja jetzt zumindest gleichwertig zur CriticalSection zu sein, wenn man einen adäquaten Wert für den SpinCount wählt.

Das passt in den meisten Fällen schon. Nur, wenn die Performance wirklich ein Problem ist, sollte man untersuchen, ob man damit etwas verbessern kann. Aber im Normalfall sollte man den SpinCount lassen wie er ist und TMonitor einfach verwenden.

SneakyBagels 7. Jun 2017 10:44

AW: Critical Section um globale Methode?
 
Durch " http://www.delphipraxis.net/1373672-post10.html " bin ich hier gelandet.
ich verwende bis heute eine globale CriticalSection in meinem Programm, welche von vielen Threads aufgerufen wird, die dieselben Daten lesen und schreiben.
Überhaupt gar kein Problem und die Performance sieht auch OK aus.

Wie würde die Implementierung eines System.TMonitor aussehen?
Wie das aussieht muss ein Object geblockt werden. Welches denn genau? Meine TThread-Klasse kann man ja nicht übergeben. Was möchte TMonitor denn sonst haben?
Muss ich hier Self (TThread-Unit) übergeben oder reicht auch einfach eine Variable? Diese Variable (Klasseninstanz) enthält alles was ich im Thread schreibend ändere.

Zacherl 7. Jun 2017 13:41

AW: Critical Section um globale Methode?
 
Zitat:

Zitat von SneakyBagels (Beitrag 1373676)
Wie das aussieht muss ein Object geblockt werden. Welches denn genau?

Die Objektinstanz dient
Delphi-Quellcode:
TMonitor
nur als Marker. Oft hat man ja eine seperate Datenklasse, auf die die Threads dann zugreifen. Hier würde ich die Instanz dieser Klasse verwenden, aber im Grunde ist es komplett egal, welches Objekt du verwendest.

SneakyBagels 7. Jun 2017 13:57

AW: Critical Section um globale Methode?
 
Genau. Ich habe eine Klasse (vorher war es ein Record).
Dieser Klasseninstanz, welche nur einmalig erzeugt wird, weise ich in den Threads ein Objekt zu mit welchem ich dann weiterarbeite.
Das Objekt enthält Strings, Integers... ganz normale Dinge.

Aktuell habe ich eine globale CS welche von allen 5 Threads verwendet wird. Klappt sehr fein. Keinerlei Konflikte.
Könnte ich dann nun einfach auf System.TMonitor.Enter(meine globale Klasseninstanz) verwenden und hätte dasselbe Ergebnis nur besser?
Was müsste ich dem Enter denn übergeben, wenn ich sowas hier habe
Delphi-Quellcode:
- TTask Anfang (eine Aufgabe bei Klick im MainMenu)
 Monitor Enter
 try
  EineFunktionWirdAufgerufen
 finally
  Monitor Exit
 end;
- TTask Ende
Dort steht aktuell auch CriticalSections. Ohne ging das schief. Würde aber gerne weg von CS und hin zu Monitor nur weiß ich nicht, was ich dort als Objekt übergeben soll.

himitsu 7. Jun 2017 14:34

AW: Critical Section um globale Methode?
 
Im Prinzip ist TMonitor die umgedrehte Implementation für mehrere TCriticalSection.

Du kannst in ein anderes Object eine CriticalSection rein tun und diese verwenden
Delphi-Quellcode:
TMyObject = class
  Lock: TCriticalSecition;
end;

MyObj.Lock.Enter
oder du nutzt das "versteckte" Feld in allen TObject, welches man über TMonitor ansprechen kann.
Delphi-Quellcode:
TMyObject = class
end;

TMonitor.Enter(MyObj); // macht dann intern quasi sowas wie ein MyObj.geheimesproperty.Enter;
// und vorher noch ein IF NOT Assigned(MyObj.geheimesproperty) THEN MyObj.geheimesproperty := TCriticalSectionÄhnlichesDing.Create;
Nur den Namen "Monitor" fand ich schon immer ein bissl "irreführend".

SneakyBagels 7. Jun 2017 14:45

AW: Critical Section um globale Methode?
 
Das Problem ist, dass ich für mein zuletzt genantes Beispiel keine Klasse habe und ich nur eine Funktion aufrufe.

Zitat:

TMonitor.Enter(MyObj); // macht dann intern quasi sowas wie ein MyObj.geheimesproperty.Enter;
// und vorher noch ein IF NOT Assigned(MyObj.geheimesproperty) THEN MyObj.geheimesproperty := TCriticalSectionÄhnlichesDing.Create;
Das verstehe ich jetzt nicht. Wofür denn nun Monitor und eine CS?

Ich glaube ich bleibe einfach bei CS - ist einfacher und nicht so ein Hexenwerk ;)

Zacherl 7. Jun 2017 15:12

AW: Critical Section um globale Methode?
 
Zitat:

Zitat von SneakyBagels (Beitrag 1373722)
Das verstehe ich jetzt nicht. Wofür denn nun Monitor und eine CS?

Grob gesagt ist es auch hier "egal", was du verwendest. Je nach Anwendungszweck können sich höchstens leichte Performanceunterschiede ergeben, aber was "besser" oder "schlechter" ist, kann man pauschal nicht sagen. Es sind einfach zwei verschiedene Paradigmen als Lösung für das selbe Problem.

Statt deiner CS könntest du dir einfach eine dummy
Delphi-Quellcode:
TObject
Instanz erstellen und darauf dann
Delphi-Quellcode:
TMonitor.Enter
anwenden. Macht aber nicht wirklich Sinn, weshalb ich hier wohl auch bei der globalen CriticalSection bleiben würde.

himitsu 7. Jun 2017 15:16

AW: Critical Section um globale Methode?
 
Zitat:

Zitat von SneakyBagels (Beitrag 1373722)
Das verstehe ich jetzt nicht. Wofür denn nun Monitor und eine CS?

Im Prinzip ist Beides das Gleiche, nur mit 'ner anderen Syntax.

Ist wie der unterschied zwischen
Delphi-Quellcode:
StringList[123]
und
Delphi-Quellcode:
StringList.Strings[123]
.



Abgesehn, dass TMonitor was "Eigenes" ist und TCriticalSection die API vom Windows kapselt, aber von der Funktion her sind die schon vergleichbar.

SneakyBagels 7. Jun 2017 15:19

AW: Critical Section um globale Methode?
 
Ok dann bleibe ich wohl bei CriticalSections.
Die sind mir vom Aufbau her so oder so logischer.
Ich selber konnte bei meinen Tests keinerlei Performanceunterschied feststellen.
Scheinen wohl nur Nanosekunden zu sein.

himitsu 7. Jun 2017 15:23

AW: Critical Section um globale Methode?
 
In Delphi gibt es noch viel mehr Implementationen, die das Gleiche wie eine CriticalSection/TMonitor erledigen.

Dann noch paar Dinger mit Zusatzfunktionen, wie z.B. den TMultiReadExclusiveWriteSynchronizer (TMREWSync), welcher parallele Lesezugriffe erlaubt und nur bei einem Schreibzugriff alles komplett sperrt.
Und für Zugriffe auf einzelne Interger, Pointer, Booleans und anderen Kleinkram gibt es mehrere Interlocked-Implementationen, welche atomare Schreibzugriffe ermöglichen.

SneakyBagels 7. Jun 2017 15:37

AW: Critical Section um globale Methode?
 
Zitat:

Dann noch paar Dinger mit Zusatzfunktionen, wie z.B. den TMultiReadExclusiveWriteSynchronizer (TMREWSync), welcher parallele Lesezugriffe erlaubt und nur bei einem Schreibzugriff alles komplett sperrt.
Das ist interessant zu wissen! Ich dachte immer nur, hier würde nur für lesenden Zugriff gesperrt.
Aber heißt das jetzt, dass sobald ich bei Verwendung von TMultiReadExclusiveWriteSynchronizer irgendwo trotzdem schreibe, nur dieser eine schreibende Zugriff geblockt wird?
Wenn ja dann wäre das ja ein recht großer Vorteil denn statt 100 von 100 "Dingern" zu blocken (wo ich 1x schreibe und 99x lese) würde hier nur 1x geblockt. Sehe ich das richtig?

Dass ich schreibenden Zugriff hier mit BeginWrite und EndWrite absichern muss ist klar. Aber wofür den lesenden?

Edit:
durch die Verwendung eines globalen TMultiReadExclusiveWriteSynchronizer und BeginWrite/EndWrite, welches ich NUR dort verwende wo ich meiner Klasseninstanz Daten zuweise, ist meine Prozedur rund 5% schneller geworden. Kann das daran liegen, dass ich nun nicht mehr rigoros alles blockiere sondern nur noch den schreibenden Zugriff?

Zacherl 7. Jun 2017 16:01

AW: Critical Section um globale Methode?
 
Zitat:

Zitat von SneakyBagels (Beitrag 1373744)
durch die Verwendung eines globalen TMultiReadExclusiveWriteSynchronizer und BeginWrite/EndWrite, welches ich NUR dort verwende wo ich meiner Klasseninstanz Daten zuweise, ist meine Prozedur rund 5% schneller geworden. Kann das daran liegen, dass ich nun nicht mehr rigoros alles blockiere sondern nur noch den schreibenden Zugriff?

Korrekt. Der
Delphi-Quellcode:
TMultiReadExclusiveWriteSynchronizer
ist für solche Szenarien besser geeignet, als
Delphi-Quellcode:
TCriticalSection
oder
Delphi-Quellcode:
TMonitor
. Solange jetzt n-Threads gleichzeitig NUR lesen, wirst du einen Performancevorteil feststellen können. Im Grunde stellt der
Delphi-Quellcode:
TMultiReadExclusiveWriteSynchronizer
jetzt nur noch sicher, dass erst alle lesenden Zugriffe abgeschlossen sind, bevor ein schreibender Zugriff stattfindet und blockiert während dieser Zeit dann alle lesenden Operationen (deshalb musst du trotzdem signalisieren, an welchen Stellen du lesen willst).

Mit
Delphi-Quellcode:
TCriticalSection
oder
Delphi-Quellcode:
TMonitor
hingegen, kann auch nur ein einziger Thread gleichzeitig lesen.

himitsu 7. Jun 2017 16:12

AW: Critical Section um globale Methode?
 
Lesezugriffe sperren sich nicht gegenseitig ... mehrere sind erlaubt.
So lange es Lesesperren gibt, warten alle Schreibanfragen.
Eine Schreibsperre sperrte alle anderen Lese und Schreibzugriffe.

Natürlich nur verwendbar für "echte" Lesezugriffe, wo wirklich nichts geschrieben wird.
z.B. Stream.Read ist zwar ein Lesezugriff, aber da bei wird der Positionszeiger geändert, was einen Schreibzugriff darstellt.

Aber sonst können mehrere ungehindert gleichzeitig lesen
und wenn Einer was schreiben will, dann ist so lange alles gesperrt.
MultiReader ist demnach besonders hilfreich, bei vielen Lesezugriffen und seltenen Schreibzugriffen.

Bei der CriticalSection und dem TMonitor ist immer alles gesperrt, egal ob lesen oder schreiben.
(nur Zugriffe aus dem selben Thread sind erlaubt, da sie per se threadsave sind und man so keinen Deadlock riskiert, wenn man in einem Thread verschachtelt Enter aufruft)

SneakyBagels 7. Jun 2017 16:58

AW: Critical Section um globale Methode?
 
Mhhh ok das heißt also

Delphi-Quellcode:
-thread-instanzen 1+2+3 (alle selber Code, nur unterschiedliche Listen sind abzuarbeiten)
begin
 schleife anfang

 _globals.aMultiReadExclusiveWriteSynchronizer.BeginWrite;
 setze globale Klasseninstanz := Liste.Items[i]
 _globals.aMultiReadExclusiveWriteSynchronizer.EndWrite;

 (a)
 ...
 lese hier
 ...
 lese dort
 ...
 lese nochmals hier
 (b)

 schleife ende
end;
So habe ich das aktuell. Jeder Thread kann der globale Klasseninstanz ein Item zuweisen. An anderen Stellen (nicht in diesem Thread) lese ich die Klasseninstanz dann aus und zeige die dahinterliegenden Daten auf der Benutzeorberfläche an.

Ist das so richtig oder müsste ich jetzt noch zusätzlich von (a) bis (b) ein _globals.aMultiReadExclusiveWriteSynchronizer.Begi nRead; und EndRead setzen?

himitsu 7. Jun 2017 17:35

AW: Critical Section um globale Methode?
 
Dort, wo lesend auf globale-Klasseninstanz zugegriffen wird, kommt natürlich ein BeginRead drumrum.

Erstmal reicht es den Lese-Zugriff auf diese Variable zu sperren,
aber wenn sich innerhalb dieser Instanz auch etwas ändern kann (Property und enthaltene Variablen), dann auch das mit einschließen.

Wenn sich an der Objektinstanz nicht gleichzeig in mehreren Thread was ändert,
also z.B. da was einstellen, dann die Variable setzen und jemand Anderes liest dann nur diese Variable, oder holt sich sie da raus (auslesen und NIL setzen),
dann kann man auch mit InterlockedExcange oder den neuen TLock-irgendwas-Klassen in der System-Unit arbeiten.

SneakyBagels 7. Jun 2017 17:39

AW: Critical Section um globale Methode?
 
Da achte ich strikt drauf. Erst ändere ich und dann lese ich. Danach wird erstmal nicht mehr geändert. Im jedem Schleifendurchgang gibt es nur eine einzige Änderung.
Wofür ist es denn wichtig den lesenden Zugriff zu sperren?
Wenn man 5 Thread-Instanzen hat, alle haben die gleichen Dinge abzuarbeiten, man aber den lesen Zugriff blockieren muss, kann man dann hier überhaupt noch von Multithreading reden?

Erste Tests zeigen, dass wenn ich überall den lesenden Zugriff blockiere der Code doppelt so lange braucht als vorher.
Den lesenden Zugriff hatte ich früher noch nie blockiert, immer nur den schreibenden.

himitsu 7. Jun 2017 17:53

AW: Critical Section um globale Methode?
 
Die Lesedinge sperren sich dabei nicht gegenseitig.

Das BeginRead ist nur dafür da, um zu verhindern, dass jemand während des Lesens etwas ändern kann.
BeginRead sperrt das BeginWrite, bzw. hält selber an, wenn gerade geschrieben wird.
Andere BeginRead werden aber durch den Read-Lock (durch BeginRead) nicht beeinflusst.

SneakyBagels 7. Jun 2017 18:02

AW: Critical Section um globale Methode?
 
Zitat:

BeginRead sperrt das BeginWrite, bzw. hält selber an, wenn gerade geschrieben wird.
Andere BeginRead werden aber durch den Read-Lock (durch BeginRead) nicht beeinflusst.
Danke! Muss ich heute Abend noch einmal ausführlich testen!

Wenn BeginRead nur dafür da ist zu verhindern, dass kein Write ausgeführt werden kann, lasse ich das vermutlich weg denn es gibt genau eine einzige Stelle an der geschrieben wird.

jaenicke 7. Jun 2017 19:50

AW: Critical Section um globale Methode?
 
Zitat:

Zitat von SneakyBagels (Beitrag 1373766)
Wenn BeginRead nur dafür da ist zu verhindern, dass kein Write ausgeführt werden kann, lasse ich das vermutlich weg denn es gibt genau eine einzige Stelle an der geschrieben wird.

Entweder du kannst sicherstellen, dass bei dem Schreibvorgang ohnehin niemand liest, dann brauchst du keinerlei Sperren (sofern beim Lesen alles threadsicher ist).

Oder du kannst nicht ausschließen, dass jemand liest während du schreibst, dann musst du dies durch Sperren sicherstellen, in dem Fall durch den TMultiReadExclusiveWriteSynchronizer. Dann musst du dem aber auch sagen wann du lesen und wann schreiben möchtest, damit er diese Sperre auch durchführen kann.

SneakyBagels 7. Jun 2017 20:06

AW: Critical Section um globale Methode?
 
Ist bei mir ganz einfach: es ist exakt eine einzige Zeile Code im Multithread-System, die schreibenden Zugriff hat.
Es gibt eine weitere solche Zeile, die hat aber nichts mit Multithreading zu tun.

Es ist egal von wo aus ich TMultiReadExclusiveWriteSynchronizer aufrufe, oder muss das aus dem Thread heraus sein der schreiben möchte?

jaenicke 8. Jun 2017 05:45

AW: Critical Section um globale Methode?
 
Zitat:

Zitat von SneakyBagels (Beitrag 1373775)
Es ist egal von wo aus ich TMultiReadExclusiveWriteSynchronizer aufrufe, oder muss das aus dem Thread heraus sein der schreiben möchte?

Ich glaube ich hole besser noch einmal weiter aus bevor ich das beantworte.

Du musst vor jedem Schreibvorgang BeginWrite aufrufen (ab da werden alle Schreib- und Lesezugriffe blockiert) und nach dessen Abschluss EndWrite (dann können wieder alle lesen).

Vor jedem Lesevorgang musst du BeginRead aufrufen (ab da wartet BeginWrite, falls es danach aufgerufen wird, auf den Abschluss des Lesevorgangs) und nach dessen Abschluss EndRead (danach kehrt BeginWrite zurück, falls es nach BeginRead aufgerufen wurde.

Auf diese Weise kann ein Schreibvorgang erst nach Abschluss aller laufenden Lesevorgänge passieren und neue Lesevorgänge erst nach dem Abschluss eines laufenden Schreibvorgangs.
Solange aber niemand schreibt, können alle parallel lesen, d.h. BeginRead..EndRead kann beliebig parallel laufen.

Um zur Frage zurückzukommen:
Im Prinzip ist es egal aus welchem Threadkontext du BeginWrite..EndWrite aufrufst solange es vor bzw. nach dem Schreibvorgang passiert. Wann der passiert, sollte ein anderer Thread aber in der Regel (es gibt Ausnahmen, ja) gar nicht kontrollieren, weshalb die praktische Antwort meistens Nein ist, auch wenn es technisch egal ist.

Genau lässt sich das aber nur sagen, wenn man den konkreten Ablauf kennt. Es würde auch abstrakt ohne Code genügen, z.B. auch als Diagramm, wer (Thread XY) wann (während andere Threads laufen? autark? kontrolliert durch einen anderen Thread? ...) was (Schreiben oder Lesen) macht, um einen Überblick zu haben.

HolgerX 8. Jun 2017 08:32

AW: Critical Section um globale Methode?
 
Hmm..

Der Zugriff erfolgt vergleichbar einer TCriticalSection.

Beispiel:

Delphi-Quellcode:
unit Unit2;

interface

uses
  SysUtils;

function LeseDaten : string;
procedure SchreibeDaten(Value : string);

implementation

var
  MSync_Global : TMultiReadExclusiveWriteSynchronizer;

  Daten_Test : string;

function LeseDaten : string;
begin
  MSync_Global.BeginRead;
  try
    Result := Daten_Test;
  finally
    MSync_Global.EndRead;
  end;
end;

procedure SchreibeDaten(Value : string);
begin
  MSync_Global.BeginWrite;
  try
    Daten_Test := Value;
  finally
    MSync_Global.EndWrite;
  end;
end;

initialization
  MSync_Global := TMultiReadExclusiveWriteSynchronizer.Create;

finalization
  MSync_Global.Free;

end.

Alle Threads (auch der Main..) nutzen nun einfach

function LeseDaten : string;
procedure SchreibeDaten(Value : string);

für den Zugriff auf 'Daten_Test : string;'.

Durch die Verwendung des globalen 'MSync_Global : TMultiReadExclusiveWriteSynchronizer;' innerhalb der Funktionen ist der Zugriff Threadsave.. ;)

(Einfach nur mal so Runtergeschrieben als minimales Beispiel ;) )
(Fehler und Irtümer nicht ausgeschlossen ;) )

SneakyBagels 8. Jun 2017 09:59

AW: Critical Section um globale Methode?
 
Zitat:

Zitat von jaenicke (Beitrag 1373780)
Genau lässt sich das aber nur sagen, wenn man den konkreten Ablauf kennt. Es würde auch abstrakt ohne Code genügen, z.B. auch als Diagramm, wer (Thread XY) wann (während andere Threads laufen? autark? kontrolliert durch einen anderen Thread? ...) was (Schreiben oder Lesen) macht, um einen Überblick zu haben.

Wie eine Seite zuvor beschrieben, hier mit Pseudo-Code.


Delphi-Quellcode:
// Andere Unit erzeugt die Threads aus dem Mainthread heraus
erzeuge Thread 1+2+3 und gebe jedem ein Teil aus einer großen Liste; // (ja ich weiß, es gibt TParallel.For :P )


// Die Thread-unit selber
-thread-instanzen 1+2+3 (alle selber Code, nur unterschiedliche Listen sind abzuarbeiten)
begin
 schleife anfang

 _globals.aMultiReadExclusiveWriteSynchronizer.BeginWrite;
 setze globale Klasseninstanz := Liste.Items[i]
 _globals.aMultiReadExclusiveWriteSynchronizer.EndWrite;

 (a)
 ...
 lese hier
 ...
 lese dort
 ...
 lese nochmals hier
 (b)

 schleife ende
end;
Wie man sieht gibt es genau eine einzige Stelle in dieser Thread-Unit, wo geschrieben wird. Alles danach ist nur noch lesen.

HolgerX 8. Jun 2017 14:54

AW: Critical Section um globale Methode?
 
Zitat:

Zitat von SneakyBagels (Beitrag 1373814)
Delphi-Quellcode:
// Andere Unit erzeugt die Threads aus dem Mainthread heraus
erzeuge Thread 1+2+3 und gebe jedem ein Teil aus einer großen Liste; // (ja ich weiß, es gibt TParallel.For :P )


// Die Thread-unit selber
-thread-instanzen 1+2+3 (alle selber Code, nur unterschiedliche Listen sind abzuarbeiten)
begin
 schleife anfang

 _globals.aMultiReadExclusiveWriteSynchronizer.BeginWrite;
 setze globale Klasseninstanz := Liste.Items[i]
 _globals.aMultiReadExclusiveWriteSynchronizer.EndWrite;

 (a)
 ...
 lese hier
 ...
 lese dort
 ...
 lese nochmals hier
 (b)

 schleife ende
end;
Wie man sieht gibt es genau eine einzige Stelle in dieser Thread-Unit, wo geschrieben wird. Alles danach ist nur noch lesen.


Um das Sync zu machen, musst du dennoch von jedem Lesen 'BeginRead' und danach 'EndRead' machen! Nur fürs Schreiben BeginWrite/EndWrite reicht nicht!


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:15 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