Ich lese gerade Jesse Storimers exzellentes Buch Mit Unix-Prozessen arbeiten . In einem Abschnitt über Trapping-Signale von Kind-Prozessen, die beendet wurden, gibt er ein Codebeispiel.
Ich habe diesen Code leicht modifiziert (siehe unten), um ein bisschen mehr Einblick in das zu bekommen, was passiert:
puts
sehen kann), wait
wird für mehrere Kinder in einer trap
-Anweisung ausgeführt (manchmal bekomme ich "Empfangen eines CHLD-Signals", gefolgt von mehreren "Kind-PID-Verlassen"). Normalerweise sieht die Ausgabe des folgenden Codes ungefähr so aus:
%Vor%Aber ab und zu bekomme ich einen Fehler wie folgt:
%Vor%Kann mir jemand erklären, was hier schief läuft?
Ich habe durch Ruby-Quellen nachgesehen, wo dieser bestimmte Fehler auftritt, und Es wird immer nur ausgelöst, wenn der aktuelle Thread versucht, eine Sperre zu erhalten, aber diese Sperre wird bereits vom aktuellen Thread übernommen. Dies bedeutet, dass die Sperrung nicht einspringend ist:
%Vor% Jetzt wissen wir wenigstens, was passiert, aber noch nicht warum und wo. Die Fehlermeldung zeigt an, dass dies während des Aufrufs von puts
geschieht. Wenn es aufgerufen wird, landet es schließlich in io_binwrite . stdout
ist nicht synchronisiert, aber gepuffert, also diese if-Bedingung ist beim ersten Aufruf erfüllt, und ein Puffer plus eine Schreibsperre für diesen Puffer wird eingerichtet. Die Schreibsperre ist wichtig, um die Atomizität von Schreibvorgängen auf stdout zu gewährleisten. Es sollte nicht passieren, dass zwei Threads, die gleichzeitig in stdout
schreiben, die Ausgabe des anderen mischen. Um zu zeigen, was ich meine:
Obwohl sich beide Threads beim Schreiben in stdout
abwechseln, wird es niemals passieren, dass ein einzelner Schreibvorgang abgebrochen wird - Sie haben immer die vollen 5 a oder b in der Sequenz. Das ist, was die Schreibsperre für gibt.
Was in Ihrem Fall schief läuft, ist eine Race-Bedingung für diese Schreibsperre. Der Elternprozess führt eine Schleife durch und schreibt jede Sekunde in stdout
("Elternteil arbeitet hart"). Aber derselbe Thread führt möglicherweise auch den Block trap
aus und versucht erneut, in stdout
zu schreiben ("Empfangen eines CHLD-Signals"). Sie können überprüfen, ob es wirklich derselbe Thread ist, indem Sie #{Thread.current}
in Ihren puts
-Anweisungen hinzufügen. Wenn diese beiden Ereignisse genau genug passieren, haben Sie die gleiche Situation wie im ersten Beispiel: Der gleiche Thread versucht zweimal, die gleiche Sperre zu erhalten, was letztendlich den Fehler auslöst.
Tags und Links ruby multithreading signals deadlock fork