Discussion:
Critical Sections
(zu alt für eine Antwort)
Christian Schmitt
2015-03-05 08:08:18 UTC
Permalink
Hallo Community,

ich bin relativ neu auf dem Gebiet der Threads, muß mich damit aber jetzt zwangsläufig auseinandersetzen. Ich habe auch schon ein wenig gelesen und mich daraufhin entschlossen mit Critical Sections zu arbeiten. Dazu hätte ich 2 Fragen an euch:

Habe ich es richtig verstanden, dass ein lesender Variablenzugriff kein Problem darstellt? Oder anders herum gefragt: Kann es zu Problemen kommen, wenn ich ohne Critical Section Variablen mit mehreren Threads auslese?

Bisher habe ich es so gelöst, dass jeder Thread eine eigene Critical Section hat. Allerdings bekomme ich ab und an timing probleme, da ja jeweils immer nur ein Thread diese Section bearbeiten darf (Was ja auch der Sinn ist).
Beispiel: Es sind 2 kritische Variablen A und B definiert, die beide von mehreren Threads benutzt werden sollen. Wenn ich nun zum setzen von A die Critical Section betrete, muß der zweite Thread auch warten, wenn er nur Variable B setzen möchte, da ja auch hierfür die CS betreten werden muß. Das würde ich gerne durche mehrere CS verhindern. Spricht da etwas dagegen? Eventuell sogar neue Klassen erfinden nach dem Motto

type
TCriticalString=class
private
FCriticalSection:TCriticalSection;
FValue:String;
procedure FSetValue(S:String);
function FGetValue:String;
public
property Value:String read FGetValue write FSetValue;
end;

...

Procedure FSetValue(S:String);
begin
FCriticalSection.Enter;
FValue:=S;
FCriticalSection.Leave;
end;

Function FGetValue:result;
begin
FCriticalSection.Enter; //Oder hier nicht, je nach dem was Ihr zu Frage 1 sagt :-)
Result:=FValue;
FCriticalSection.Leave;
end;


Oder handele ich mir damit andere Probleme ein?

Gruß
Alfred Gemsa
2015-03-05 09:14:15 UTC
Permalink
Post by Christian Schmitt
Habe ich es richtig verstanden, dass ein lesender Variablenzugriff kein Problem darstellt? Oder anders herum gefragt: Kann es zu Problemen kommen, wenn ich ohne Critical Section Variablen mit mehreren Threads auslese?
Bisher habe ich es so gelöst, dass jeder Thread eine eigene Critical Section hat. Allerdings bekomme ich ab und an timing probleme, da ja jeweils immer nur ein Thread diese Section bearbeiten darf (Was ja auch der Sinn ist).
Beispiel: Es sind 2 kritische Variablen A und B definiert, die beide von mehreren Threads benutzt werden sollen. Wenn ich nun zum setzen von A die Critical Section betrete, muß der zweite Thread auch warten, wenn er nur Variable B setzen möchte, da ja auch hierfür die CS betreten werden muß. Das würde ich gerne durche mehrere CS verhindern. Spricht da etwas dagegen? Eventuell sogar neue Klassen erfinden nach dem Motto
Ohne jetzt auf dein Problem direkt zu antworten, kennst du

Threads_mit_Delphi.pdf von Michael Puff?

Da wird dir geholfen.

HTH, ALfred
Christian Schmitt
2015-03-05 10:01:57 UTC
Permalink
Post by Alfred Gemsa
Ohne jetzt auf dein Problem direkt zu antworten, kennst du
Threads_mit_Delphi.pdf von Michael Puff?
Oha, nein kenne ich nicht. Werde mich mal durcharbeiten... Danke! Wenn aber trotzdem noch jemand seinen Senf zu meinen Fragen geben will/kann, herzlich Willkommen ;-)

Gruß
Hans-Peter Diettrich
2015-03-05 14:21:22 UTC
Permalink
Post by Christian Schmitt
Habe ich es richtig verstanden, dass ein lesender Variablenzugriff
kein Problem darstellt? Oder anders herum gefragt: Kann es zu
Problemen kommen, wenn ich ohne Critical Section Variablen mit
mehreren Threads auslese?
Jein ;-)

Konkurrierende lesende Zugriffe sind überhaupt kein Problem an sich.
Aber vielleicht soll ja die Variable irgendwann auch mal geändert
werden, sonst wäre es ja eine Konstante, und dann kann das Lesen während
eines Schreibvorgangs Murks liefern. Solche überlappende Zugriffe können
aber nur bei nicht-atomaren Zugriffen auftreten, d.h. auf Datentypen mit
mehreren Bytes. Mehr dazu in der Literatur.

Für einzelne (unstrukturierte) Variablen hätte ich einen Vorschlag, der
ohne Critical Sections auskommt. Dafür wird jede Variable in ein
array[boolean] umgewandelt, z.B. in einen
type TMyVar = record
Value: array[boolean] of WelcherTypAuchImmer;
Current: boolean;
end;

Dann erfolgt das Lesen mit:
x := rec.Value[rec.Current]; //oder über Methode/Property des Records
und beim Schreiben wird zuerst der neue Wert eingetragen, und dann der
Index geändert:
rec.Value[not rec.Current] := NeuerWert;
rec.Current := not rec.Current;

Da Zugriffe auf boolean Variablen atomar sind, bekommt man beim Lesen
nur entweder den alten oder neuen Wert - je nachdem, ob der alte oder
neue Wert von Current gelesen wurde - aber keine Mischung aus beiden.

Probleme könnten dann nur noch auftreten, wenn der Wert gleichzeitig von
mehreren Threads geändert werden kann. Wenn man das ausschließen kann,
ist alles in trockenen Tüchern. Ansonsten muß nur das Schreiben
synchronisiert werden, was hier ggf. einfacher mit einem Semaphor statt
mit Critical Section implementiert werden kann. Ich weiß nicht, ob man
bei Semaphoren noch um Assembler herumkommt (CMPXCHG...).[1]

Bei strukturierten Datentypen (insbesondere Klassen!) kann es schwierig
sein, die zwei Kopien sauber zu implementieren. Da würde ich
vorschlagen, die kritischen Felder in den Klassen durch obige Records zu
ersetzen, nicht die gesamten Instanzen.


[1] Vorschlag zu Semaphor, ungetestet!
Noch ein Byte LockCount zu obigem Record hinzufügen. Dann Schreiben mit
sowas wie:
if INCR(rec.LockCount) = 1
then ... //neuen Wert schreiben
//else Wert wegwerfen, wir sind zu spät gekommen
dec(rec.LockCount);

INCR sei irgendwas, das inkrementiert *und* den neuen Wert zurückgibt,
ggf. InterlockedIncrement o.ä.

Möglicherweise muß der Thread, der vergeblich versucht hat, den Wert zu
ändern, noch mehr tun, um alle seine nun ungültigen Werte durch die
aktuellen Werte (des anderen Threads) zu ersetzen. Das sollte aber durch
eine besser geeignete Organisation von vornherein vermieden werden.
Alternativ könnte der Thread seine Änderung dennoch durchführen, sobald
der andere Thread fertig ist, wenn das Sinn macht - z.B. beim Schreiben
in eine Logdatei/stream, Anhängen an eine Liste usw. Das wäre aber bei
Verwendung von Critical Sections auch nicht anders, soll also hier nicht
weiter betrachtet werden.


Alle Angaben aus dem leeren Bauch, Kommentare herzlich willkommen :-)

DoDi
Christian Schmitt
2015-03-08 10:39:16 UTC
Permalink
Post by Hans-Peter Diettrich
Für einzelne (unstrukturierte) Variablen hätte ich einen Vorschlag, der
ohne Critical Sections auskommt. Dafür wird jede Variable in ein
array[boolean] umgewandelt, z.B. in einen
type TMyVar = record
Value: array[boolean] of WelcherTypAuchImmer;
Current: boolean;
end;
Klingt auch nicht schlecht. Vorallem hat man kein Zeit-Problem, da niemand warten muß. Entweder man hat noch den alten Wert oder schon den neuen.
Post by Hans-Peter Diettrich
Probleme könnten dann nur noch auftreten, wenn der Wert gleichzeitig von
mehreren Threads geändert werden kann. Wenn man das ausschließen kann,
ist alles in trockenen Tüchern.
Kann ich leider nicht bei allen Variablen, für die werde ich dann wahrscheinlich um CS nicht herumkommen.
Post by Hans-Peter Diettrich
Ansonsten muß nur das Schreiben
synchronisiert werden, was hier ggf. einfacher mit einem Semaphor statt
mit Critical Section implementiert werden kann. Ich weiß nicht, ob man
bei Semaphoren noch um Assembler herumkommt (CMPXCHG...).[1]
OK, weiß jetzt nicht warum Semaphoren "ggf. einfacher" sind, ich habe mal kurz [1] gelesen, da finde ich Critical Sections aber einfacher: Create, enter, leave, free - Fertig ;-)
Post by Hans-Peter Diettrich
Bei strukturierten Datentypen (insbesondere Klassen!) kann es schwierig
sein, die zwei Kopien sauber zu implementieren. Da würde ich
vorschlagen, die kritischen Felder in den Klassen durch obige Records zu
ersetzen, nicht die gesamten Instanzen.
Das hatte ich auch so gemeint. Alle kritischen Strings, Integers o.ä. ersetzen, in meinem Vorschlag halt mit einer TCriticalString, TCriticalInteger o.ä. Klasse, die ich halt wie im o.P. aufbauen wollte. Aber wie gesagt, deine Idee mit dem Record ist auch gut!

Danke schon mal für deine Einschätzung!

Gruß
Chris

[1] http://edn.embarcadero.com/article/29908

Loading...