Nehmen wir an, ich habe zwei Variablen, protected_var1
und protected_var2
. Nehmen wir weiter an, dass diese Variablen über mehrere Threads aktualisiert werden und ziemlich unabhängig davon sind, dass normalerweise das eine oder das andere, aber nicht beide bearbeitet werden - so haben beide ihre eigenen Mutex-Wächter für Effizienz.
Angenommen:
- Sperren Sie Mutexe immer in der richtigen Reihenfolge (Mutex1, dann Mutex2) in meinem Code in Bereichen, in denen beide Sperren erforderlich sind.
- Beide Mutexe werden an vielen anderen Stellen von ihnen selbst benutzt (wie zB mutex1 sperren, oder einfach mutex2 sperren).
Macht die Reihenfolge, in der ich die Mutexe am Ende einer Funktion mit beiden freischalte, einen Unterschied in dieser Situation?
%Vor%Vor langer Zeit wurde mir in einem Interview eine Frage bezüglich dieser Situation gestellt, und ich stellte fest, dass die Antwort ja war - die Reihenfolge dieser beiden Freischaltungen ist von Bedeutung. Ich kann nicht für das Leben von mir herausfinden, wie sich ein Deadlock daraus ergeben könnte, wenn die Schlösser immer in der gleichen Reihenfolge erhalten werden, wo immer beide benutzt werden.
Ich kann nicht für das Leben von mir herausfinden, wie sich ein Deadlock daraus ergeben könnte, wenn die Schlösser immer in der gleichen Reihenfolge erhalten werden, wo immer beide benutzt werden.
Unter diesen Umständen glaube ich nicht, dass die Reihenfolge, in der die Mutexe entsperrt werden, die Ursache für einen Deadlock sein könnte.
Da pthread_mutex_unlock()
nicht blockiert, werden beide Mutexe immer entsperrt, unabhängig von der Reihenfolge der beiden Aufrufe.
Beachten Sie, dass wenn Sie versuchen, Sperren zwischen die beiden Entsperrungsanrufe zu erwerben, dies das Bild vollständig ändern kann.
Die Reihenfolge sollte keine Rolle spielen, solange Sie nicht versuchen zu erwerben eine weitere Sperre zwischen den Releases. Das Wichtigste ist immer erwirb die Schlösser in der gleichen Reihenfolge; Andernfalls riskieren Sie einen Stillstand.
BEARBEITEN:
Um die Einschränkung zu erweitern: Sie müssen eine strenge Reihenfolge festlegen
die Mutexe, z.B. mutex1
geht vor mutex2
(aber diese Regel ist gültig für
beliebig viele Mutexe). Sie können nur eine Sperre für einen Mutex anfordern, wenn Sie
Halte nicht einen Mutex, der in der Reihenfolge danach kommt; z.B. du darfst nicht
Fordern Sie eine Sperre für mutex1
an, wenn Sie eine Sperre für mutex2
haben. Jederzeit
Diese Regeln werden respektiert, Sie sollten in Sicherheit sein. In Bezug auf
Freigabe, wenn Sie mutex1
freigeben, dann versuchen Sie es vorher wieder zu erwerben
Wenn Sie mutex2
freigeben, haben Sie die Regel verletzt. In dieser Hinsicht kann es
ein Vorteil in Bezug auf eine Stapel-ähnliche Reihenfolge sein: zuletzt erworben ist
immer der erste veröffentlicht. Aber es ist eine Art indirekter Effekt: der
Regel ist, dass Sie eine Sperre auf mutex1
nicht anfordern können, wenn Sie eine an halten
%Code%. Unabhängig davon, ob Sie bei mutex2
eine Sperre hatten
hat die Sperre für mutex1
oder nicht erhalten.
Es spielt keine Rolle für die Korrektheit der Verriegelung. Der Grund ist, dass, selbst wenn ein anderer Thread darauf wartet, Mutex1 und dann Mutex2 zu sperren, der schlimmste Fall ist, dass er sofort geplant wird, sobald Sie Mutex1 freigeben (und Mutex1 erwirbt). Dann blockiert es das Warten auf Mutex2, und der Thread, nach dem du fragst, wird freigegeben, sobald es wieder eingeplant ist, und es gibt keinen Grund, der nicht bald eintreten sollte (sofort, wenn dies die einzigen beiden Threads sind) / p>
Es kann also in der exakten Situation zu kleinen Kosten in der Leistung kommen, im Vergleich dazu, wenn Sie zuerst Mutex2 freigegeben haben und es daher nur einen Neuterminierungsvorgang gab. Nichts, was man normalerweise vorhersagen oder befürchten müsste, sondern alles innerhalb der Grenzen von "Planung ist oft nicht deterministisch".
Die Reihenfolge, in der Sie die Sperren freigeben, könnte jedoch die Planung im Allgemeinen beeinflussen. Angenommen, zwei Threads warten auf Ihren Thread und einer von ihnen ist auf Mutex1 blockiert, während der andere auf Mutex2 blockiert ist. Es kann sich herausstellen, dass, egal welche Sperre Sie zuerst loslassen, dieser Thread zuerst ausgeführt wird, nur weil Ihr Thread seine Begrüßung überlebt hat (mehr als eine ganze Zeitscheibe verbraucht) und daher entschifft wird, sobald etwas anderes ausgeführt werden kann. Aber das kann nicht zu einem Fehler in einem ansonsten korrekten Programm führen: Sie dürfen sich nicht auf Ihren entplanten Thread verlassen, sobald er die erste Sperre aufhebt. Die Reihenfolge der beiden wartenden Threads, die beide gleichzeitig ausgeführt werden, wenn Sie mehrere Kerne haben, oder die beiden, die sich auf einem Kern abwechseln, müssen alle gleich sicher sein, unabhängig davon, in welcher Reihenfolge Sie die Sperren freigeben.
Die Reihenfolge der Entsperrung kann nicht zu Deadlocks führen. Wenn jedoch die Gelegenheit gegeben wird, empfehle ich, sie in umgekehrter Verriegelungsreihenfolge zu entsperren. Dies hat einen vernachlässigbaren Einfluss auf die Ausführung des Codes. Entwickler sind jedoch daran gewöhnt, in Bereichen zu denken, und Bereiche "schließen" in umgekehrter Reihenfolge. Sie sehen sie in umgekehrter Reihenfolge, um einfach an die Lösungsschranken zu denken. Das bringt mich zum zweiten Punkt, nämlich dass es in den meisten Fällen am sichersten ist, die direkten Aufrufe zum Sperren und Entsperren durch einen Stack-basierten Wächter zu ersetzen, der sie für Sie aufruft. Dies führt zu dem höchsten Grad an Korrektheit für die geringste mentale Anstrengung, und ist auch sicher in der Gegenwart von Ausnahmen (die wirklich mit einer manuellen Entsperrung misten können)!
Ein einfacher Wächter (es gibt viele da draußen ... das ist nur ein schnelles Selbstdrehen):
%Vor%Nein, das macht nichts. Ein Deadlock kann daraus nicht resultieren; Beide Entriegelungsoperatoren sind garantiert erfolgreich (Bar-Heap-Korruption oder ähnliche Probleme).
Die Reihenfolge der Entsperrung ist hier kein Problem, aber die Reihenfolge der Sperrung kann ein Problem sein.
Überlegen Sie:
%Vor% Dies kann zu einem Deadlock führen, da foo
mutex1
gesperrt hat und jetzt auf mutex2
wartet, während bar
mutex2
gesperrt hat und jetzt auf mutex1
wartet. Es ist also eine gute Idee, sicherzustellen, dass geschachtelte Mutex-Sperren immer in derselben Reihenfolge gesperrt werden.
Solange var1 und var2 gesperrt sind, sind sie in derselben Reihenfolge gesperrt, in der Sie sicher sind, unabhängig von der freigebenden Reihenfolge. In der Reihenfolge, in der sie gesperrt wurden, wird das RAII-Freigabeverhalten in STL- und BOOST-Sperren freigegeben.
Tags und Links c++ multithreading deadlock pthreads