Forum: PC-Programmierung CriticalSection-Rätsel


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
Thread A
1
  Lock.Acquire;
2
  CancelIoEx(FDevice, nil);
3
  FileWrite(FDevice, SendBuf, SizeOf(SendBuf));
4
  Lock.Release;

Thread B
1
  Lock.Acquire;
2
  Lock.Release;
3
  Len := FileRead(FDevice, Buf, SizeOf(Buf));

Ziel der Übung ist es zu verhindern daß auf keinen Fall beide Threads 
gleichzeitig in der FileRead/Write API stecken, insbesondere hängt es 
wenn A gerade schreibt und dann B kommt und FileRead aufruft, dann 
verklemmt sich die Windows-API derart daß FileWrite hängenbleibt.

Erschwernd kommt hinzu daß FileRead blockiert, wenn also B zuerst da war 
muss A ihm mit CancelIOEx erstmal einen Fußtritt verpassen.

So wie es oben steht scheint es zwar zu laufen, aber ich sehe da immer 
noch eine Möglichkeit, ich hab mal mit Pfeilen eingezeichnet wo sich die 
Ausführung theoretisch befinden könnte, das ist zwar unwahrscheinlich 
aber durchaus möglich. Wenn jetzt A ins FileWrite läuft und dann B ins 
FileRead dann verklemmt sich der darunter liegende Treiber wieder.

Thread A
1
  <-- A ist hier
2
3
  Lock.Acquire;
4
  CancelIoEx(FDevice, nil);
5
  FileWrite(FDevice, SendBuf, SizeOf(SendBuf));
6
  Lock.Release;

Thread B
1
  Lock.Acquire;
2
  Lock.Release;
3
4
  <-- B ist hier
5
6
  Len := FileRead(FDevice, Buf, SizeOf(Buf));

Hat jemand ne wasserdichte Idee, gerne auch mit 2 oder 3 Locks?

: Bearbeitet durch User
von Clemens L. (c_l)


Bewertung
0 lesenswert
nicht lesenswert
Wenn FDevice vor gleichzeitigen Zugriffen geschützt werden sollen, dann 
sollte eigentlich auch das FileRead in den Lock.

von Cyblord -. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Ja das Locking ist einfach falsch, egal wie 
wahrscheinlich/unwahrscheinlich der Fall des Eintretens ist. Read gehört 
vor Lock Release.

Hier zum Üben:

https://deadlockempire.github.io/

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
Abradolf L. schrieb:
> Ja das Locking ist einfach falsch, egal wie
> wahrscheinlich/unwahrscheinlich der Fall des Eintretens ist. Read gehört
> vor Lock Release.

Dann kann aber CancelIOEx() nicht mehr aufgerufen werden. So einfach ist 
es also nicht. Nächster Vorschlag?

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Bernd K. schrieb:
> Abradolf L. schrieb:
>> Ja das Locking ist einfach falsch, egal wie
>> wahrscheinlich/unwahrscheinlich der Fall des Eintretens ist. Read gehört
>> vor Lock Release.
>
> Dann kann aber CancelIOEx() nicht mehr aufgerufen werden. So einfach ist
> es also nicht. Nächster Vorschlag?

Warum?

von Wilhelm M. (wimalopaan)


Bewertung
1 lesenswert
nicht lesenswert
Bernd K. schrieb:

> Thread B
>
1
>   Lock.Acquire;
2
>   Lock.Release;
3
>   Len := FileRead(FDevice, Buf, SizeOf(Buf));
4
>
>

So ist der Lock doch wertlos.

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Bernd K. schrieb:
>> Abradolf L. schrieb:
>>> Ja das Locking ist einfach falsch, egal wie
>>> wahrscheinlich/unwahrscheinlich der Fall des Eintretens ist. Read gehört
>>> vor Lock Release.
>>
>> Dann kann aber CancelIOEx() nicht mehr aufgerufen werden. So einfach ist
>> es also nicht. Nächster Vorschlag?
>
> Warum?

Weil Thread B das Lock hält.

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Bernd K. schrieb:
>
>> Thread B
>>>   Lock.Acquire;
>>   Lock.Release;
>>   Len := FileRead(FDevice, Buf, SizeOf(Buf));
>> >
>
> So ist der Lock doch wertlos.

Ja, deshalb hab ich ja die Frage gepostet.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Niemals blockierende Systemcalls in CS!

Deswegen muss Du schauen, das FileRead nicht blockiert.
Also muss Du abfragen ob Du lesen kannst. Deswegen brauchst hier einen 
bedingten kritischen Abschnitt -> ConditionVariable

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
So gehts. Manchmal sieht man den Wald vor Bäumen nicht.

A
1
  while not Lock.TryEnter do
2
    CancelIoEx(FDevice, nil);
3
  FileWrite(FDevice, SendBuf, SizeOf(SendBuf));
4
  Lock.Release;

B
1
  Lock.Acquire;
2
  Len := FileRead(FDevice, Buf, SizeOf(Buf));
3
  Lock.Release;

Die Busy-Loop hat zwar noch nen faden Beigeschmack, aber so gehts 
zumindest.

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Deswegen muss Du schauen, das FileRead nicht blockiert.

Geht nicht.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Klar geht das.
Aus Unix mit select().

Vllt erst Größe bestimmen.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Bewertung
-2 lesenswert
nicht lesenswert
Bernd K. schrieb:

> A
>
1
>   while not Lock.TryEnter do
2
>     CancelIoEx(FDevice, nil);
3
>   FileWrite(FDevice, SendBuf, SizeOf(SendBuf));
4
>   Lock.Release;
5
>
>
> B
>
1
>   Lock.Acquire;
2
>   Len := FileRead(FDevice, Buf, SizeOf(Buf));
3
>   Lock.Release;
4
>
>
> Die Busy-Loop hat zwar noch nen faden Beigeschmack, aber so gehts
> zumindest.

Das ist Mist.

von Andreas H. (andreas_h16)


Bewertung
1 lesenswert
nicht lesenswert
auf die harte Methode?

Thread A
1
  CancelIoEx(FDevice, nil);
2
  Lock.Acquire;
3
  FileWrite(FDevice, SendBuf, SizeOf(SendBuf));
4
  Lock.Release;

Thread B
1
  Lock.Acquire;
2
  Len := FileRead(FDevice, Buf, SizeOf(Buf));
3
  Lock.Release;

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Andreas H. schrieb:
> auf die harte Methode?
>
> Thread A
>
>
1
>   CancelIoEx(FDevice, nil);
2
>   Lock.Acquire;
3
>   FileWrite(FDevice, SendBuf, SizeOf(SendBuf));
4
>   Lock.Release;
5
>
>
> Thread B
>
>
1
>   Lock.Acquire;
2
>   Len := FileRead(FDevice, Buf, SizeOf(Buf));
3
>   Lock.Release;
4
>

Nein. B ist falsch. Kein blockierenden Aufrufe in kritischen 
Abschnitten.

Du musst einen "Begingten kritschen Abschnitt" aus B machen, sonst droht 
DeadLock.

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Klar geht das.
> Aus Unix mit select().

Blockierendes Read ist an sich keine schlechte Sache (wenn es 
funktioniert), das Aufteilen getrennter Abläufe in getrennte Threads ist 
eine Alternative zur großen Statemachine, eine bewußte 
Designentscheidung die (IMHO) oftmals zu lesbarerem Code führen kann.

Das einzige das mir hier in diesem konkreten Fall in die Suppe spuckt 
ist der schrottige HID-Treiber in Windows 7 der nicht thread-safe zu 
sein scheint und sich weghängt wenn man gleichzeitig FileRead und 
FileWrite auf dem selben Handle macht, unter Linux funktioniert das 
vollkommen reibungslos und die beiden Threads sind dort vollkommen 
voneinander entkoppelt und kommen sich nicht in die Quere.

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Das ist Mist.

Warum?

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Nein.

Du liest von einer Queue. Angenommen die Queue ist leer und B der erste. 
Dann blockiert B und verursacht den Deadlock. A wird am Mutex blockiert.

Nochmal: des wegen brauchst Du einen "Begingten kritischen Abschnitt" in 
B.

Etwa:

Mutex lock:
CondiditionVar cv;

B

lock.acquire();
while (file.empty()) {
   cv.wait();
}
file.read();
lock.release();

: Bearbeitet durch User
von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
Andreas H. schrieb:
> auf die harte Methode?
>
> Thread A
>   CancelIoEx(FDevice, nil);
>   Lock.Acquire;
>   FileWrite(FDevice, SendBuf, SizeOf(SendBuf));
>   Lock.Release;
>
> Thread B
>   Lock.Acquire;
>   Len := FileRead(FDevice, Buf, SizeOf(Buf));
>   Lock.Release;

Ich male mal die Pfeile ein:

Thread A
  CancelIoEx(FDevice, nil);

  <-- A ist hier

  Lock.Acquire;
  FileWrite(FDevice, SendBuf, SizeOf(SendBuf));
  Lock.Release;

Thread B
  Lock.Acquire;

  <-- B ist hier

  Len := FileRead(FDevice, Buf, SizeOf(Buf));
  Lock.Release;

Jetzt läuft B ins blockierende read und A wartet hilflos bis in alle 
Ewigkeit.

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> file.empty()

Diese Funktion gibt es leider nicht, sonst wärs ja einfach :-(

von Wilhelm M. (wimalopaan)


Bewertung
-1 lesenswert
nicht lesenswert
Bernd K. schrieb:

>
> Jetzt läuft B ins blockierende read und A wartet hilflos bis in alle
> Ewigkeit.

Das versuch ich ihm doch die ganze Zeit zu sagen ...

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> while (file.empty())

Inwiefern ist das anders als ein blockierendes Read?

von Wilhelm M. (wimalopaan)


Bewertung
-1 lesenswert
nicht lesenswert
Bernd K. schrieb:
> Wilhelm M. schrieb:
>> file.empty()
>
> Diese Funktion gibt es leider nicht, sonst wärs ja einfach :-(

Vielleicht so etwas ähnliches wie select() oder poll()? Oder was 
anderes?

Wenn das wirklich der Fall sein sollte, was ich nicht glaube (was ist 
das eigentlich für ein API???), dann muss Du auf non-blocking IO 
umstellen und in B pollen.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Bernd K. schrieb:
> Wilhelm M. schrieb:
>> while (file.empty())
>
> Inwiefern ist das anders als ein blockierendes Read?

Schau Dir mal ein Buch über BEGINGTE kritische Abschnitte an.

file.empty() sollte ja wohl nicht blockieren, oder?

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:

> Das versuch ich ihm doch die ganze Zeit zu sagen ...

Deshalb ja meine neue Lösung:

>>   while not Lock.TryEnter do
>>     CancelIoEx(FDevice, nil);
>>   FileWrite(FDevice, SendBuf, SizeOf(SendBuf));
>>   Lock.Release;

Jetzt kann A den B aus dem Read rauskicken und gleichzeitig das Lock 
bekommen. Das läuft jetzt schon seit einer Stunde mit viel Traffic in 
beide Richtungen durch und hat sich noch nicht aufgehängt.

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> file.empty() sollte ja wohl nicht blockieren, oder?

Du hast aber

while (file.empty())

geschrieben und das blockiert sehr wohl.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Sicher? Das war doch PseudoCode ...

Was ist das für ein API?

: Bearbeitet durch User
von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Sicher? Das war doch PseudoCode ...
>
> Was ist das für ein API?

Windows

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Und was entspricht Deiner Meinung nach file.empty() oder file.is_empty() 
oder (file.size() == 0) im Windows API ???

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Und was entspricht Deiner Meinung nach file.empty() oder file.is_empty()
> oder (file.size() == 0) im Windows API ???

Sag Du es mir?

Das Device ist ein generic HID device am USB-Bus, Treiber ist von 
Windows. Ich kenne keine Möglichkeit ohne zu lesen festzustellen ob man 
lesen kann, es sei denn ich krempel das alles (nur für den Windows-Port) 
auf Overlapped-IO um und krempel alles von innen nach außen und darauf 
hab ich ehrlich gesagt keinen Bock, das sprengt den Rahmen.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Leider bin ich in der *nix-Welt groß geworden ...

Es gibt aber m.E. auch unter Windows die Möglichkeit, den 
Datei-Deskriptor bzw. File-Handle auf ein non-blocking-IO umzustellen. 
Unter *nix geht das mit einem ioctl() / fnctl().

Dann könnte Thread B folgendes durchführen (Pseudo-Code):
1
Mutex lock;
2
ConditionVariable cv(lock);
3
4
while (file.read(...) == NO_DATA_AVAILABLE) {
5
    cv.await();
6
}
7
file.read(...)

Thread A:
1
file.write(...);
2
cv.signal();

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Es gibt aber m.E. auch unter Windows die Möglichkeit, den
> Datei-Deskriptor bzw. File-Handle auf ein non-blocking-IO umzustellen.

Der Windows API bietet für asychrone I/O wahlweise ein completion event
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365467(v=vs.85).aspx
als auch eine completion routine:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365468(v=vs.85).aspx
Das dürfte er vorhin mit Operlapped-IO gemeint haben.

: Bearbeitet durch User
von c-hater (Gast)


Bewertung
-2 lesenswert
nicht lesenswert
Bernd K. schrieb:

> Ziel der Übung ist es zu verhindern daß auf keinen Fall beide Threads
> gleichzeitig in der FileRead/Write API stecken

Wenn das das Ziel war und du TATSÄCHLICH hilfreiche Hinweise haben 
willst, musst du schon die Implementierung von "Lock" offen legen. Für 
mich sieht das jedenfalls stark nach etwas aus, was jemand benutzt, der 
weder "Lock" versteht noch generell das Windows-FileAPI.

Ich hingegen verstehe zwar letzteres durchaus sehr gut, muss aber 
bezüglich Hilfe passen, weil "Lock" nur äußerst unzureichend beschrieben 
wurde. Eine einfache Win32-CriticalSection (wie es das Subject des 
Threads suggeriert) scheint es ja nicht zu sein...

von Wilhelm M. (wimalopaan)


Bewertung
1 lesenswert
nicht lesenswert
c-hater schrieb:
> Bernd K. schrieb:
>
>> Ziel der Übung ist es zu verhindern daß auf keinen Fall beide Threads
>> gleichzeitig in der FileRead/Write API stecken
>
> Wenn das das Ziel war und du TATSÄCHLICH hilfreiche Hinweise haben
> willst, musst du schon die Implementierung von "Lock" offen legen.

Auf gar keinen Fall.

Die Synchronisationsprimitive wie Mutex (Lock), Semaphor, 
ConditionVariable, MessageQueue sind in ihrer Semantik wohl definiert in 
der Informatik. Genauso wie die Begriffe (unbedingter / bedingter) 
Kritische Abschnitt.

: Bearbeitet durch User
von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
c-hater schrieb:
> Eine einfache Win32-CriticalSection (wie es das Subject des
> Threads suggeriert) scheint es ja nicht zu sein...

Es ist ein Wrapper um eine native CriticalSection auf dem jeweils 
zugrundeliegenden OS (in dem Falle Windows). Oder ums genauer zu sagen 
es ist eine Instanz von TCriticalSection aus der Unit SyncObjs.


Die Lösung zu dem Problem hab ich ja bereits gefunden:

A
1
repeat
2
3
  [...]
4
5
  while not Lock.TryEnter do
6
    CancelIoEx(FDevice, nil);
7
  FileWrite(FDevice, SendBuf, SizeOf(SendBuf));
8
  Lock.Release;
9
until Terminated;

B
1
repeat
2
  Lock.Acquire;
3
  Len := FileRead(FDevice, Buf, SizeOf(Buf));
4
  Lock.Release;
5
6
  [...]
7
8
until Terminated;

Der Grund warum ich mich daran festgebissen habe war der daß ich mich 
darauf versteift habe auf Teufel komm raus eine Lösung ohne das while zu 
finden, eine geschickte Anordnung von zwei oder mehr Locks die das selbe 
bewirkt. Das simple while not Lock.TryEnter war der sprichwörtliche Wald 
den ich dann vor Bäumen aus irgendeinem Grund nicht sehen wollte.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
c-hater schrieb im Beitrag #4861410:
> Wilhelm M. schrieb:
>
>> Die Synchronisationsprimitive wie Mutex (Lock), Semaphor,
>> ConditionVariable, MessageQueue sind in ihrer Semantik wohl definiert in
>> der Informatik. Genauso wie die Begriffe (unbedingter / bedingter)
>> Kritische Abschnitt.
>
> Und schon geloosed.
>
> "Primitives" haben nicht in jedem Kontext das gleiche "Innenleben".

Das hat niemand behauptet und das muss auch gar nicht so sein.

> Ein
> Win32-Mutex z.B. ist defininitiv etwas deutlich anderes als eine
> Win32-CriticalSection.

Ja klar, die Win32-Mutex-Implementierung, die das Konzept eines Mutex 
umsetzt, ist bspw. zwischen Threads unterschiedlicher Prozesse 
verwendbar. Die Win32-CriticalSection-Implementierung realisiert auch 
das Konzept eines Mutex, der aber leichtgewichtiger ist in vielen 
Aspekten, aber nur zwischen Threads desselben Prozesses verwendet werden 
kann.

Im übrigen ist der Name Win32-"CriticalSection" sehr schlecht von MS 
gewählt, weil es eben ein Mutex ist.
Dagegen ist eine sog. Critical Section ein Begriff aus der Informatik, 
der einen Teil eines Ausführungspfades bezeichnet, der nicht-nebenläufig 
ausgeführt werden muss. Zu dessen Schutz kann man Mutexe, Semphore, 
RWLocks, Monitore, ... einsetzen.

> Nur C-only-Wichsern

Wen meinst Du damit?

Und ausserdem geht es hier doch gar nicht um "C".

> ist das nicht klar, die allein halten ihren
> Sprach-Standard für quasi gottgegeben. Das ist er aber eben NICHT. Was
> allerdings eigentlich selbst den absolut Dümmsten dieser Vollidioten
> klar sein müsste, schließlich gibt es ja nicht einmal EINEN allgemein
> gültigen C-Standard,

den gibt es sehr wohl. Allerdings gibt es eben auch nicht-konforme 
Compiler. Die ewige Zwickmühle Standard <-> Umsetzung.

>der auch nur die Sprache selber vollständig
> beschreibt, geschweige denn das Verhalten von irgendwelchen Syncobjects
> aus irgendwelchen schrägen hinzugelinkten Libs...

Das müssen die Bibliotheken machen, das hat mit der Sprache bzw. konkret 
mit "C" hier nichts zu tun. Die C-Standard-Bibliothek macht dies bspw. 
für die o.g. Synchronisationsprimitive (aka Synchronisationskonzepte):

http://en.cppreference.com/w/c/thread

Alternativ kommen natürlich auch die durch die Plattform direkt zur 
Verfügung gestellten Schnittstellen wie der _POSIX_THREADS Anteil aus 
IEEE 1003.1 oder eben aus Win32 in Frage.

von c-hater (Gast)


Bewertung
-5 lesenswert
nicht lesenswert
Wilhelm M. schrieb:

> Das hat niemand behauptet

Mir scheint, du hast das behauptet. Nur indirekt, aber trotzdem sehr 
eindeutig, nämlich indem du behauptest hast, das die Implementierung von 
"Lock" (was auch immer das gewesen sein mag), keine Rolle spielen würde.

Hier in deiner Antwort gibst du aber ebenso indirekt zu, dass es sehr 
wohl eine Rolle spielen könnte, nämlich zumindest dadurch, indem du 
zumindest den unterschiedlichen Wirkungsbereich von (Win32-) 
CriticalSection und Mutex anerkennst. Die Unterschiede sind aber noch 
deutlich weitgehender als das, was du zugestehst, selbst wenn man beides 
auf Threads eines einzigen Prozesses anwendet, kann man sie nicht in 
jeder Hinsicht "synonym" benutzen. Daran ändert auch die Tatsache 
nichts, dass die besonderen Features eines Win32-Mutex innerhalb eines 
Prozesses nicht sinnvoll nutzbar sind, man muss bei der Programmierung 
trotzdem die Besonderheiten beachten. Es kommt also sehr wohl darauf an, 
wie "Lock" implementiert ist. Erstmal darauf, ob es nun tatsächlich auf 
Win32-Mutex oder -CriticalSection aufsetzt, und zweitens, falls es auf 
einem Mutex aufsetzt, ob dessen Besonderheiten bei der Implementierung 
hinreichend gewürdigt wurden.

> Im übrigen ist der Name Win32-"CriticalSection" sehr schlecht von MS
> gewählt, weil es eben ein Mutex ist.

Hmm... Das ist wohl Ansichtssache. Aber ich habe keine Lust, über 
Nomenklaturen zu streiten. Aber ich gestehe dir gern zu, dass die 
MS-Nomenklatur an sehr vielen Stellen zumindest diskussionswürdig ist. 
Darüber kann man sich ärgern, darüber kann man diskutieren, aber 
letztlich setzt doch das API die Fakten für die Nomenklatur. Und hier 
ging es definitiv um das Windows-API und nicht um abstrakte Blasen aus 
dem Informatik-Akademiebetrieb, was schon im OP ganz eindeutig 
klargestellt wurde...

>> Nur C-only-Wichsern
>
> Wen meinst Du damit?

Z.B. die Leute, die nicht wissen, was bei der Synchronisation "hinter 
den Kulissen" abgeht. Also all die arme Schweine, die höchstens zufällig 
mal MT-Anwendungen so hinbekommen, dass kein Deadlock mehr darin lauert. 
Ist ja schließlich schon für Leute schwer genug, das zu vermeiden, die 
wirklich verstehen, was da im Detail passiert...

> Und ausserdem geht es hier doch gar nicht um "C".

Doch, irgendwie schon. C ist halt nicht für MT designed und bietet 
keinerlei Unterstützung zur Vermeidung selbst der primitivsten 
Standardfehler in diesem Umfeld. Wie auch für viele andere 
Standardfehler nicht, angefangen vom oberprimitivsten Integer-Overflow 
bis hin zum Buffer-Overflow...

[...C-Standard...]
> den gibt es sehr wohl.

Nein, es gibt eben nicht nur einen, sondern mehrere. Und noch sehr viel 
mehr Interpretationen jedes dieser "Standards"...

> Das müssen die Bibliotheken machen

Wie sollen sie das tun, wenn sich die verschiedenen Standards derselben 
Sprache in Details logisch widersprechen? Das ist aus Gründen der 
formalen Logik völlig unmöglich. Man kann nicht auf eine indifferente 
Basis eine logisch konsistente Implementierung aufsetzen. Das geht 
nunmal einfach nicht.

> das hat mit der Sprache bzw. konkret
> mit "C" hier nichts zu tun.

Oh doch. Wenn du das nicht erkennst, kannst du einfach kein C.

Allerdings, auch wenn alles wahr ist, was ich schrieb: Ob das nun im 
Zshg. mit der Implementierung dieses ominösen "Lock" irgendeine Rolle 
spielt, kann man eben nur dann erkennen, wenn eben diese Implementierung 
gezeigt wird. Und eben genau darauf zielte ich ab...

Capisce?!

von Wilhelm M. (wimalopaan)


Bewertung
3 lesenswert
nicht lesenswert
c-hater schrieb:

>
>>> Nur C-only-Wichsern
>>
>> Wen meinst Du damit?
>
> Z.B. die Leute, die nicht wissen, was bei der Synchronisation "hinter
> den Kulissen" abgeht.

Und warum bezeichnest Du Leute, von denen Du (!) meinst, dass sie auf 
einem bestimmten Gebiet (noch) nicht so viel Wissen haben, als Wichser? 
Das ist eine grundlose Beleidigung! In anderen ist das ein 
Ausschlußgrund, aber Du bist ja deswegen wahrscheinlich nicht 
angemeldet.

> Also all die arme Schweine, die höchstens zufällig

Und die nächste Ausfälligkeit ...

>> Und ausserdem geht es hier doch gar nicht um "C".
>
> Doch, irgendwie schon.

Schau bitte ganz oben in diesem Thread ...

> C ist halt nicht für MT designed

aber auch nicht dagegen, sondern agnostisch. Wohingegen die 
Standard-C-Bibliothek eben mit atomics und der thread-support-library 
genau diese Unterstützung bieten.

> Standardfehler in diesem Umfeld. Wie auch für viele andere
> Standardfehler nicht, angefangen vom oberprimitivsten Integer-Overflow
> bis hin zum Buffer-Overflow...

Und wenn das der Fall wäre, dann geht das Lamentieren um den 
Laufzeit-Overhead wieder los.

> [...C-Standard...]
>> den gibt es sehr wohl.
>
> Nein, es gibt eben nicht nur einen, sondern mehrere.

Welche denn noch ausser ISO/IEC 9899 in den Revisionen (C11, C99, C95, 
C89/90)?

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Dagegen ist eine sog. Critical Section ein Begriff aus der Informatik,
> der einen Teil eines Ausführungspfades bezeichnet, der nicht-nebenläufig
> ausgeführt werden muss.

Wieso, passt doch? Genau das tut Enter/ExitCriticalSection doch. Zumal 
es nicht einfach bloss eine System-Mutex ist, sondern es erst einmal mit 
einem Spinlock versucht.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Wilhelm M. schrieb:
>> Dagegen ist eine sog. Critical Section ein Begriff aus der Informatik,
>> der einen Teil eines Ausführungspfades bezeichnet, der nicht-nebenläufig
>> ausgeführt werden muss.
>
> Wieso, passt doch?

Nein, oben ist Win32-CriticalSection ein konkreter Datentyp einer 
bestimmten Bibliothek.

> Zumal
> es nicht einfach bloss eine System-Mutex ist, sondern es erst einmal mit
> einem Spinlock versucht.

Ein Mutex ist zunächst mal ein abstrakter Datentyp (ADT), der eine 
bestimmte Funktion bereitstellt, mit dem man kritische Abschnitte 
realisieren kann. Wie die konkrete Realisierung eines Mutex 
(posix_mutex_t, futex, binäres Semaphor, ..., win32-CriticalSection) das 
macht, ist erst mal irrelevant.

Ein kritischer Abschnitt hingegen ist wieder ein anderer Begriff, der 
zunächst einmal nur festlegt, das eine Anweisungsfolge nicht nebenläufig 
ausgeführt werden darf. Ob dazu ein Mutex oder irgendetwas anderes der 
möglichen Synchronisationsprimitive benutzt wird, spielt keine Rolle.

von (prx) A. K. (prx)


Bewertung
1 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Nein, oben ist Win32-CriticalSection ein konkreter Datentyp einer
> bestimmten Bibliothek.

Fast. Der heisst CRITICAL_SECTION. Schon die Schreibweise weist ihn als 
Datentyp aus, insofern besteht keine Verwechselungsgefahr. Dass die 
Namen von Datentypen in Windows bisweilen einen Bezug zu dem haben, was 
damit angestellt wird, finde ich nicht so schlimm.

Dazu gehören Funktionen des API, wie EnterCriticalSection und 
ExitCriticalSection. Die heissen gradewegs so, wie das was sie tun.

> Wie die konkrete Realisierung eines Mutex
> (posix_mutex_t, futex, binäres Semaphor, ..., win32-CriticalSection) das
> macht, ist erst mal irrelevant.

Das gilt gleichermassen für einen kritischen Abschnitt. Also wie man den 
realisiert.

> Ob dazu ein Mutex oder irgendetwas anderes der möglichen
> Synchronisationsprimitive benutzt wird, spielt keine Rolle.

Eben. Weshalb CRITICAL_SECTION mindestens auf der abstrakten Ebene kein 
Mutex-Handle ist, sondern ein Handle für die Verwaltung eines kritischen 
Abschnitts.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Wilhelm M. schrieb:
>> Nein, oben ist Win32-CriticalSection ein konkreter Datentyp einer
>> bestimmten Bibliothek.
>
> Fast. Der heisst CRITICAL_SECTION. Schon die Schreibweise weist ihn als
> Datentyp aus,

sag ich doch, wobei ich die Schreibweise von dem anderen, ungemein 
freundlichen Post (c-hater) übernommen habe.

> insofern besteht keine Verwechselungsgefahr. Dass die
> Namen von Datentypen in Windows bisweilen einen Bezug zu dem haben, was
> damit angestellt wird, finde ich nicht so schlimm.

Ich finde es grauenhaft: weil es einfach unterschiedliche Dinge sind. 
Das eine ein DT, das andere ein Code-Abschnitt.

> Dazu gehören Funktionen des API, wie EnterCriticalSection und
> ExitCriticalSection. Die heissen gradewegs so, wie das was sie tun.

Das ist ja wieder ok.
Man könnte auch in bspw. C++ eine RAII-Style Locker so benennen: dieser 
wiederum verwendet eine Mutex-Realisierung um einen kritischen Abschnitt 
herzustellen. Aber der Mutex selbst ist eben kein kritischer Abschnitt!

>> Wie die konkrete Realisierung eines Mutex
>> (posix_mutex_t, futex, binäres Semaphor, ..., win32-CriticalSection) das
>> macht, ist erst mal irrelevant.
>
> Das gilt gleichermassen für einen kritischen Abschnitt. Also wie man den
> realisiert.

Auch das habe ich ja gesagt: als Mutex, um einen kritischen Abschnitt 
herzustellen, könnte man auch ein binäres Semaphor oder eine 
MessageQueue verwenden.

>> Ob dazu ein Mutex oder irgendetwas anderes der möglichen
>> Synchronisationsprimitive benutzt wird, spielt keine Rolle.

s.o.

> Eben. Weshalb CRITICAL_SECTION mindestens auf der abstrakten Ebene kein
> Mutex-Handle ist, sondern ein Handle für die Verwaltung eines kritischen
> Abschnitts.

Was soll das aussagen?

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
>> Eben. Weshalb CRITICAL_SECTION mindestens auf der abstrakten Ebene kein
>> Mutex-Handle ist, sondern ein Handle für die Verwaltung eines kritischen
>> Abschnitts.
>
> Was soll das aussagen?

Das: CRITICAL_SECTION ist der Datentyp eines Handle für 
Enter/ExitCriticalSection. Was dahinter steckt ist mir aus reiner 
Programmiersicht schietegal.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Wilhelm M. schrieb:
>>> Eben. Weshalb CRITICAL_SECTION mindestens auf der abstrakten Ebene kein
>>> Mutex-Handle ist, sondern ein Handle für die Verwaltung eines kritischen
>>> Abschnitts.
>>
>> Was soll das aussagen?
>
> Das: CRITICAL_SECTION ist der Datentyp eines Handle für
> Enter/ExitCriticalSection. Was dahinter steckt ist mir aus reiner
> Programmiersicht schietegal.

Na, jetzt drehen wir uns im Kreis: das hatte ich ganz oben schon gesagt 
(allerdings als Antwort c-hater).

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.