Eines der häufigsten Probleme beim Schreiben von Multithread-Anwendungen sind die Rennbedingungen.
Meine Fragen an die Community sind:
Wie ist die Rennbedingung?
Wie erkennt man sie?
Wie gehst du damit um?
Wie verhindern Sie schließlich, dass sie auftreten?
multithreading
concurrency
terminology
race-condition
bmurphy1976
quelle
quelle
Antworten:
Eine Race-Bedingung tritt auf, wenn zwei oder mehr Threads auf gemeinsam genutzte Daten zugreifen können und gleichzeitig versuchen, diese zu ändern. Da der Thread-Planungsalgorithmus jederzeit zwischen Threads wechseln kann, wissen Sie nicht, in welcher Reihenfolge die Threads versuchen, auf die gemeinsam genutzten Daten zuzugreifen. Daher hängt das Ergebnis der Datenänderung vom Thread-Planungsalgorithmus ab, dh beide Threads "rennen" um auf die Daten zuzugreifen / diese zu ändern.
Probleme treten häufig auf, wenn ein Thread ein "check-then-act" ausführt (z. B. "check", wenn der Wert X ist, dann "act", um etwas zu tun, das vom Wert X abhängt) und ein anderer Thread etwas mit dem Wert in tut zwischen dem "Scheck" und dem "Akt". Z.B:
Der Punkt ist, y könnte 10 sein, oder es könnte alles sein, abhängig davon, ob ein anderer Thread x zwischen der Prüfung und der Handlung geändert hat. Sie haben keine wirkliche Art zu wissen.
Um das Auftreten von Race-Bedingungen zu verhindern, sperren Sie normalerweise die freigegebenen Daten, um sicherzustellen, dass jeweils nur ein Thread auf die Daten zugreifen kann. Dies würde ungefähr so etwas bedeuten:
quelle
Eine "Race-Bedingung" liegt vor, wenn Multithread-Code (oder auf andere Weise paralleler Code), der auf eine gemeinsam genutzte Ressource zugreifen würde, dies so tun könnte, dass unerwartete Ergebnisse erzielt werden.
Nehmen Sie dieses Beispiel:
Wenn Sie 5 Threads hätten, die diesen Code gleichzeitig ausführen, würde der Wert von x NICHT 50.000.000 betragen. Es würde in der Tat mit jedem Lauf variieren.
Dies liegt daran, dass jeder Thread, um den Wert von x zu erhöhen, Folgendes tun muss: (offensichtlich vereinfacht)
Jeder Thread kann sich zu jedem Zeitpunkt in einem beliebigen Schritt in diesem Prozess befinden, und sie können aufeinander treten, wenn eine gemeinsam genutzte Ressource beteiligt ist. Der Status von x kann von einem anderen Thread während der Zeit zwischen dem Lesen von x und dem Zurückschreiben geändert werden.
Angenommen, ein Thread ruft den Wert von x ab, hat ihn jedoch noch nicht gespeichert. Ein anderer Thread kann auch den gleichen Wert von x abrufen (da noch kein Thread ihn geändert hat), und dann würden beide den gleichen Wert (x + 1) wieder in x speichern !
Beispiel:
Rennbedingungen können vermieden werden, indem vor dem Code, der auf die gemeinsam genutzte Ressource zugreift, eine Art Sperrmechanismus verwendet wird:
Hier lautet die Antwort jedes Mal 50.000.000.
Weitere Informationen zum Sperren finden Sie unter: Mutex, Semaphor, kritischer Abschnitt, freigegebene Ressource.
quelle
Sie planen, um 17 Uhr ins Kino zu gehen. Sie erkundigen sich um 16 Uhr nach der Verfügbarkeit der Tickets. Der Vertreter sagt, dass sie verfügbar sind. Sie entspannen sich und erreichen das Ticketfenster 5 Minuten vor der Show. Ich bin sicher, Sie können sich vorstellen, was passiert: Es ist ein volles Haus. Das Problem lag hier in der Dauer zwischen der Prüfung und der Aktion. Sie haben um 4 Uhr nachgefragt und um 5 Uhr gehandelt. In der Zwischenzeit hat sich jemand anderes die Tickets geholt. Das ist eine Rennbedingung - speziell ein "Check-then-Act" -Szenario der Rennbedingungen.
Überprüfung des religiösen Codes, Multithread-Unit-Tests. Es gibt keine Verknüpfung. Es gibt nur wenige Eclipse-Plugins, aber noch nichts Stabiles.
Das Beste wäre, nebenwirkungsfreie und zustandslose Funktionen zu erstellen und so viel wie möglich unveränderliche Funktionen zu verwenden. Das ist aber nicht immer möglich. Die Verwendung von java.util.concurrent.atomic, gleichzeitige Datenstrukturen, ordnungsgemäße Synchronisierung und akteursbasierte Parallelität helfen also.
Die beste Ressource für Parallelität ist JCIP. Weitere Details zur obigen Erklärung finden Sie hier .
quelle
Es gibt einen wichtigen technischen Unterschied zwischen Rennbedingungen und Datenrennen. Die meisten Antworten scheinen davon auszugehen, dass diese Begriffe gleichwertig sind, aber nicht.
Ein Datenrennen tritt auf, wenn zwei Anweisungen auf denselben Speicherort zugreifen. Mindestens einer dieser Zugriffe ist ein Schreibvorgang, und es findet kein Ereignis statt, bevor zwischen diesen Zugriffen bestellt wird. Was nun ein Ereignis vor der Bestellung ausmacht, wird viel diskutiert, aber im Allgemeinen führen Ulock-Lock-Paare mit derselben Sperrvariablen und Wartesignalpaare mit derselben Bedingungsvariablen zu einem Ereignis vor der Bestellung.
Eine Rennbedingung ist ein semantischer Fehler. Es ist ein Fehler, der in dem Timing oder die Ordnung der Ereignisse das führt zu fehlerhaftem Programm auftritt Verhalten .
Viele Rennbedingungen können (und werden tatsächlich) durch Datenrennen verursacht, dies ist jedoch nicht erforderlich. Tatsächlich sind Datenrennen und Rennbedingungen weder die notwendige noch die ausreichende Bedingung für einander. Dieser Blog-Beitrag erklärt den Unterschied auch sehr gut anhand eines einfachen Beispiels für Bankgeschäfte. Hier ist ein weiteres einfaches Beispiel , das den Unterschied erklärt.
Nachdem wir die Terminologie festgelegt haben, versuchen wir, die ursprüngliche Frage zu beantworten.
Angesichts der Tatsache, dass es sich bei den Rennbedingungen um semantische Fehler handelt, gibt es keine allgemeine Möglichkeit, sie zu erkennen. Dies liegt daran, dass es keine Möglichkeit gibt, ein automatisiertes Orakel zu haben, das im allgemeinen Fall zwischen korrektem und falschem Programmverhalten unterscheiden kann. Die Rennerkennung ist ein unentscheidbares Problem.
Andererseits haben Datenrennen eine genaue Definition, die sich nicht unbedingt auf die Korrektheit bezieht, und daher kann man sie erkennen. Es gibt viele Arten von Datenrennen-Detektoren (statische / dynamische Datenrennen-Erkennung, Lockset-basierte Datenrennen-Erkennung, Vorher-basierte Datenrennen-Erkennung, Hybrid-Datenrennen-Erkennung). Ein hochmoderner dynamischer Datenrenndetektor ist ThreadSanitizer, der in der Praxis sehr gut funktioniert.
Die Handhabung von Datenrennen erfordert im Allgemeinen eine gewisse Programmierdisziplin, um Zwischenzeiten zwischen den Zugriffen auf gemeinsam genutzte Daten zu verursachen (entweder während der Entwicklung oder sobald sie mit den oben genannten Tools erkannt wurden). Dies kann durch Sperren, Bedingungsvariablen, Semaphoren usw. erfolgen. Es können jedoch auch verschiedene Programmierparadigmen wie das Weiterleiten von Nachrichten (anstelle des gemeinsam genutzten Speichers) verwendet werden, um Datenrennen durch Konstruktion zu vermeiden.
quelle
Eine Art kanonische Definition lautet " wenn zwei Threads gleichzeitig auf denselben Speicherort zugreifen und mindestens einer der Zugriffe ein Schreibzugriff ist ". In der Situation kann der "Leser" -Thread den alten oder den neuen Wert erhalten, je nachdem, welcher Thread "das Rennen gewinnt". Dies ist nicht immer ein Fehler - tatsächlich tun dies einige wirklich haarige Low-Level-Algorithmen absichtlich -, aber es sollte im Allgemeinen vermieden werden. @Steve Gury gibt ein gutes Beispiel dafür, wann es ein Problem sein könnte.
quelle
Eine Rennbedingung ist eine Art Fehler, der nur unter bestimmten zeitlichen Bedingungen auftritt.
Beispiel: Stellen Sie sich vor, Sie haben zwei Threads, A und B.
In Thread A:
In Thread B:
Wenn Thread A unmittelbar nach der Überprüfung, dass object.a nicht null ist, vorbelegt wird, reicht B aus
a = 0
, und wenn Thread A den Prozessor gewinnt, führt er eine "Division durch Null" durch.Dieser Fehler tritt nur auf, wenn Thread A direkt nach der if-Anweisung vorbelegt wird. Er ist sehr selten, kann aber auftreten.
quelle
Die Rennbedingungen hängen nicht nur mit der Software zusammen, sondern auch mit der Hardware. Eigentlich wurde der Begriff ursprünglich von der Hardware-Industrie geprägt.
Laut Wikipedia :
Die Softwareindustrie hat diesen Begriff unverändert übernommen, was das Verständnis etwas erschwert.
Sie müssen einige Ersetzungen vornehmen, um es der Software-Welt zuzuordnen:
Race Condition in der Softwareindustrie bedeutet also, dass "zwei Threads" / "zwei Prozesse" gegeneinander antreten, um "einen gemeinsamen Zustand zu beeinflussen", und das Endergebnis des gemeinsamen Zustands hängt von einem subtilen Zeitunterschied ab, der durch einen bestimmten verursacht werden kann Startreihenfolge für Thread / Prozess, Planung von Threads / Prozessen usw.
quelle
Eine Race-Bedingung ist eine Situation bei gleichzeitiger Programmierung, in der zwei gleichzeitige Threads oder Prozesse um eine Ressource konkurrieren und der resultierende Endzustand davon abhängt, wer die Ressource zuerst erhält.
quelle
Rennbedingungen treten in Multithread-Anwendungen oder Multiprozesssystemen auf. Eine Race-Bedingung ist im Grunde genommen alles, was davon ausgeht, dass zwei Dinge, die sich nicht im selben Thread oder Prozess befinden, in einer bestimmten Reihenfolge ablaufen, ohne dass Schritte unternommen werden, um dies sicherzustellen. Dies geschieht häufig, wenn zwei Threads Nachrichten übergeben, indem Mitgliedsvariablen einer Klasse festgelegt und überprüft werden, auf die beide zugreifen können. Es gibt fast immer eine Race-Bedingung, wenn ein Thread den Ruhezustand aufruft, um einem anderen Thread Zeit zum Beenden einer Aufgabe zu geben (es sei denn, dieser Ruhezustand befindet sich in einer Schleife mit einem Überprüfungsmechanismus).
Tools zur Verhinderung von Rennbedingungen hängen von der Sprache und dem Betriebssystem ab, aber einige gemeinsame Tools sind Mutexe, kritische Abschnitte und Signale. Mutexe sind gut, wenn Sie sicherstellen möchten, dass Sie der einzige sind, der etwas tut. Signale sind gut, wenn Sie sicherstellen möchten, dass jemand anderes etwas getan hat. Das Minimieren gemeinsam genutzter Ressourcen kann auch dazu beitragen, unerwartete Verhaltensweisen zu vermeiden
Das Erkennen von Rennbedingungen kann schwierig sein, aber es gibt ein paar Anzeichen. Code, der stark vom Schlaf abhängt, ist anfällig für Rennbedingungen. Überprüfen Sie daher zunächst, ob im betroffenen Code Anrufe zum Schlafen vorliegen. Das Hinzufügen besonders langer Schlafzeiten kann auch zum Debuggen verwendet werden, um zu versuchen, eine bestimmte Reihenfolge von Ereignissen zu erzwingen. Dies kann nützlich sein, um das Verhalten zu reproduzieren, um festzustellen, ob Sie es durch Ändern des Timings der Dinge verschwinden lassen können, und um eingerichtete Lösungen zu testen. Der Ruhezustand sollte nach dem Debuggen entfernt werden.
Das Signaturzeichen dafür, dass eine Racebedingung vorliegt, ist, wenn es ein Problem gibt, das nur zeitweise auf einigen Maschinen auftritt. Häufige Fehler sind Abstürze und Deadlocks. Mit der Protokollierung sollten Sie in der Lage sein, den betroffenen Bereich zu finden und von dort aus zu arbeiten.
quelle
Microsoft hat tatsächlich einen sehr detaillierten Artikel zu dieser Frage der Rennbedingungen und Deadlocks veröffentlicht. Die am meisten zusammengefasste Zusammenfassung wäre der Titelabsatz:
quelle
Die Situation, in der der Prozess entscheidend von der Reihenfolge oder dem Zeitpunkt anderer Ereignisse abhängt.
Beispielsweise benötigen sowohl Prozessor A als auch Prozessor B identische Ressourcen für ihre Ausführung.
Es gibt Tools, um den Rennzustand automatisch zu erkennen:
Die Rennbedingungen können von Mutex oder Semaphoren behandelt werden . Sie fungieren als Sperre, die es einem Prozess ermöglicht, eine Ressource basierend auf bestimmten Anforderungen zu erwerben, um den Rennzustand zu verhindern.
Es gibt verschiedene Möglichkeiten, um Rennbedingungen zu verhindern, z. B. die Vermeidung kritischer Abschnitte .
quelle
Eine Race-Bedingung ist eine unerwünschte Situation, die auftritt, wenn ein Gerät oder System versucht, zwei oder mehr Vorgänge gleichzeitig auszuführen. Aufgrund der Art des Geräts oder Systems müssen die Vorgänge jedoch in der richtigen Reihenfolge ausgeführt werden richtig gemacht.
Im Computerspeicher oder -speicher kann eine Race-Bedingung auftreten, wenn Befehle zum Lesen und Schreiben einer großen Datenmenge fast zum gleichen Zeitpunkt empfangen werden und die Maschine versucht, einige oder alle alten Daten zu überschreiben, während diese alten Daten noch vorhanden sind lesen. Das Ergebnis kann eines oder mehrere der folgenden Ereignisse sein: ein Computerabsturz, eine "illegale Operation", eine Benachrichtigung und ein Herunterfahren des Programms, Fehler beim Lesen der alten Daten oder Fehler beim Schreiben der neuen Daten.
quelle
Hier ist das klassische Beispiel für einen Kontostand, das Neulingen hilft, Threads in Java unter den Rennbedingungen leicht zu verstehen:
quelle
Sie können Race Condition verhindern , wenn Sie "Atomic" -Klassen verwenden. Der Grund ist nur, dass der Thread die Operation get und set nicht trennt. Das folgende Beispiel ist:
Als Ergebnis haben Sie 7 in Link "ai". Sie haben zwar zwei Aktionen ausgeführt, aber die beiden Vorgänge bestätigen denselben Thread und kein anderer Thread wird dies stören, das bedeutet keine Race-Bedingungen!
quelle
Versuchen Sie dieses grundlegende Beispiel, um den Zustand des Rennens besser zu verstehen:
quelle
Sie möchten nicht immer eine Rennbedingung verwerfen. Wenn Sie ein Flag haben, das von mehreren Threads gelesen und geschrieben werden kann, und dieses Flag von einem Thread auf "erledigt" gesetzt wird, sodass der andere Thread die Verarbeitung stoppt, wenn das Flag auf "erledigt" gesetzt ist, möchten Sie dieses "Rennen" nicht Bedingung "beseitigt werden. In der Tat kann dieser als gutartige Rennbedingung bezeichnet werden.
Wenn Sie jedoch ein Tool zur Erkennung des Rennzustands verwenden, wird es als schädlicher Rennzustand erkannt.
Weitere Details zum Rennzustand finden Sie hier, http://msdn.microsoft.com/en-us/magazine/cc546569.aspx .
quelle
Stellen Sie sich eine Operation vor, bei der die Anzahl angezeigt werden muss, sobald die Anzahl erhöht wird. Das heißt, sobald CounterThread den Wert erhöht, muss DisplayThread den kürzlich aktualisierten Wert anzeigen.
Ausgabe
Hier erhält CounterThread die Sperre häufig und aktualisiert den Wert, bevor DisplayThread ihn anzeigt. Hier besteht eine Racebedingung. Race Condition kann mithilfe von Synchronzation gelöst werden
quelle
Eine Race-Bedingung ist eine unerwünschte Situation, die auftritt, wenn zwei oder mehr Prozesse gleichzeitig auf die gemeinsam genutzten Daten zugreifen und diese ändern können. Sie ist aufgetreten, weil widersprüchliche Zugriffe auf eine Ressource aufgetreten sind. Ein kritisches Abschnittsproblem kann zu Rennbedingungen führen. Um kritische Zustände innerhalb des Prozesses zu lösen, haben wir jeweils nur einen Prozess ausgeführt, der den kritischen Abschnitt ausführt.
quelle