Frage Was ist eine Race-Bedingung?


Beim Schreiben von Multithread-Anwendungen ist eines der am häufigsten auftretenden Probleme Rennbedingungen.

Meine Fragen an die Community sind:

Was ist eine Race-Bedingung? Wie finden Sie sie? Wie gehst du mit ihnen um? Wie verhindern Sie schließlich, dass sie auftreten?


736
2017-08-29 15:55


Ursprung


Antworten:


Eine Race-Bedingung tritt auf, wenn zwei oder mehr Threads auf gemeinsam genutzte Daten zugreifen können und versuchen, sie gleichzeitig zu ändern. Da der Threadplanungsalgorithmus jederzeit zwischen Threads wechseln kann, ist die Reihenfolge nicht bekannt, in der die Threads versuchen, auf die freigegebenen Daten zuzugreifen. Daher ist das Ergebnis der Datenänderung von dem Thread-Scheduling-Algorithmus abhängig, d. H. Beide Threads "rasen", um auf die Daten zuzugreifen / sie zu ändern.

Probleme treten oft auf, wenn ein Thread ein "check-then-act" durchführt (zB "check" wenn der Wert X ist, dann "act" um etwas zu tun, das davon abhängt, dass der Wert X ist) und ein anderer Thread etwas mit dem Wert in tut zwischen dem "check" und dem "act". Z.B:

if (x == 5) // The "Check"
{
   y = x * 2; // The "Act"

   // If another thread changed x in between "if (x == 5)" and "y = x * 2" above,
   // y will not be equal to 10.
}

Der Punkt ist, y könnte 10 sein, oder es könnte alles sein, abhängig davon, ob ein anderer Thread x zwischen dem Check und dem Act geändert hat. Du hast keine wirkliche Art zu wissen.

Um das Auftreten von Race Conditions zu verhindern, würden Sie normalerweise die freigegebenen Daten sperren, um sicherzustellen, dass immer nur ein Thread auf die Daten zugreifen kann. Dies würde etwa so aussehen:

// Obtain lock for x
if (x == 5)
{
   y = x * 2; // Now, nothing can change x until the lock is released. 
              // Therefore y = 10
}
// release lock for x

917
2017-08-29 16:05



Eine "Race-Bedingung" liegt vor, wenn multithreading-fähiger (oder anderweitig paralleler) Code, der auf eine gemeinsam genutzte Ressource zugreifen würde, dies so tun könnte, dass er unerwartete Ergebnisse verursacht.

Nimm dieses Beispiel:

for ( int i = 0; i < 10000000; i++ )
{
   x = x + 1; 
}

Wenn Sie 5 Threads gleichzeitig ausgeführt hätten, 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, damit jeder Thread den Wert von x erhöht, sie Folgendes tun müssen: (vereinfacht, offensichtlich)

Rufen Sie den Wert von x ab
Fügen Sie 1 zu diesem Wert hinzu
Speichern Sie diesen Wert auf x

Jeder Thread kann zu jedem Zeitpunkt in einem beliebigen Schritt in diesem Prozess sein und sie können aufeinander treten, wenn eine gemeinsam genutzte Ressource beteiligt ist. Der Zustand von x kann während der Zeit zwischen dem Lesen von x und dem Zurückschreiben durch einen anderen Thread geändert werden.

Nehmen wir an, ein Thread ruft den Wert von x ab, speichert ihn jedoch noch nicht. Ein anderer Thread kann auch die gleich Wert von x (weil noch kein Thread es geändert hat) und dann würden sie beide speichern gleich Wert (x + 1) zurück in x!

Beispiel:

Thread 1: liest x, Wert ist 7
Thread 1: addiere 1 zu x, Wert ist jetzt 8
Thread 2: liest x, Wert ist 7
Thread 1: speichert 8 in x
Thread 2: addiert 1 zu x, Wert ist jetzt 8
Faden 2: speichert 8 in x

Race-Bedingungen können vermieden werden, indem eine Art von Verriegelung Mechanismus vor dem Code, der auf die freigegebene Ressource zugreift:

for ( int i = 0; i < 10000000; i++ )
{
   //lock x
   x = x + 1; 
   //unlock x
}

Hier kommt die Antwort jedes Mal als 50.000.000 heraus.

Weitere Informationen zum Sperren finden Sie unter: Mutex, Semaphor, kritischer Abschnitt, freigegebene Ressource.


176
2017-08-29 17:01



Was ist ein Rennzustand?

Sie planen, um 17 Uhr in einen Film zu gehen. Sie erkundigen sich um die Verfügbarkeit der Tickets um 16 Uhr. Der Vertreter sagt, dass sie verfügbar sind. Sie entspannen und erreichen das Ticketfenster 5 Minuten vor der Show. Ich bin sicher, Sie können erraten, was passiert: Es ist ein volles Haus. Das Problem lag hier in der Dauer zwischen dem Check und der Aktion. Du hast bei 4 nachgefragt und bei 5 gehandelt. In der Zwischenzeit hat jemand anderes die Tickets gegriffen. Das ist eine Race-Bedingung - speziell ein "check-then-act" -Szenario der Rennbedingungen.

Wie finden Sie sie?

Religiöse Überprüfung, Multithreading-Unit-Tests. Es gibt keine Abkürzung. Es gibt ein paar Eclipse-Plugins, die auf diesem erscheinen, aber noch nichts stabiles.

Wie gehst du damit um und verhinderst du?

Am besten wäre es, side-effect-freie und statusfreie Funktionen zu erstellen und möglichst viele immutable zu verwenden. Aber das ist nicht immer möglich. Die Verwendung von java.util.concurrent.atomic, gleichzeitige Datenstrukturen, korrekte Synchronisation und akteursbasierte Gleichzeitigkeit helfen dabei.

Die beste Ressource für Parallelität ist JCIP. Sie können auch mehr bekommen Details zur obigen Erklärung hier.


111
2017-10-04 21:20



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 den gleichen Speicherort zugreifen, mindestens einer dieser Zugriffe ein Schreibvorgang ist und es keinen gibt passiert vor der Bestellung unter diesen Zugriffen. Nun, was ein Vorkommnis vor der Bestellung ausmacht, ist Gegenstand vieler Debatten, aber im allgemeinen induzieren ulock-lock-Paare auf der gleichen Sperrvariablen und Warte-Signal-Paare auf der gleichen Bedingungsvariablen eine Vor-Vor-Reihenfolge.

Eine Race Condition ist ein semantischer Fehler. Es ist ein Fehler, der beim Timing oder bei der Reihenfolge von Ereignissen auftritt, was zu einem fehlerhaften Programm führt Verhalten.

Viele Rennbedingungen können (und sind tatsächlich) durch Datenrennen verursacht werden, aber dies ist nicht notwendig. In der Tat sind Datenrennen und Rennbedingungen weder die notwendige noch die hinreichende Bedingung füreinander. Dies Blog Post erklärt auch den Unterschied sehr gut, mit einem einfachen Bank Transaktionsbeispiel. Hier ist ein weiteres einfaches Beispiel Das erklärt den Unterschied.

Nun, da wir die Terminologie niedergeschrieben haben, wollen wir versuchen, die ursprüngliche Frage zu beantworten.

Angesichts der Tatsache, dass Race-Bedingungen semantische Fehler sind, gibt es keine allgemeine Möglichkeit, sie zu erkennen. Dies liegt daran, dass es keine Möglichkeit gibt, ein automatisiertes Orakel zu haben, das korrektes vs. falsches Programmverhalten im allgemeinen Fall unterscheiden kann. Die Erkennung von Rennen ist ein unentscheidbares Problem.

Auf der anderen Seite haben Data Races eine präzise Definition, die nicht unbedingt mit der Korrektheit zu tun hat, und daher kann man sie auch erkennen. Es gibt viele Arten von Datenrace-Detektoren (statische / dynamische Datenrennerkennung, Lockset-basierte Datenrennerkennung, Vor-Ereignis-basierte Datenrennerkennung, Hybriddatenrennerkennung). Ein dynamischer Datenrace-Detektor nach dem Stand der Technik ist ThreadSanitizer das funktioniert in der Praxis sehr gut.

Die Handhabung von Datenrennen im Allgemeinen erfordert einige Programmierdisziplin, um Zufallsflanken zwischen Zugriffen auf gemeinsam genutzte Daten zu induzieren (entweder während der Entwicklung oder sobald sie unter Verwendung der oben erwähnten Tools erkannt werden). dies kann durch Sperren, Zustandsvariablen, Semaphore usw. erfolgen. Es können jedoch auch andere Programmierparadigmen wie Nachrichtenübergabe (anstelle von gemeinsam genutztem Speicher) verwendet werden, die Datenrassen durch Konstruktion vermeiden.


52
2017-08-29 08:45



Eine Art von kanonischer Definition ist "wenn zwei Threads gleichzeitig auf denselben Ort im Speicher zugreifen und mindestens einer der Zugriffe ein Schreibvorgang ist. "In der Situation kann der" Leser "-Thread den alten Wert oder den neuen Wert bekommen, abhängig davon, welcher Thread" das Rennen gewinnt ". Dies ist nicht immer ein Fehler - tatsächlich tun einige wirklich haarige Low-Level-Algorithmen das weiter Zweck - aber es sollte im Allgemeinen vermieden werden. @ Steve Gury geben ein gutes Beispiel, wenn es ein Problem sein könnte.


32
2017-08-29 16:21



Eine Race Condition ist eine Art Bug, der nur unter bestimmten zeitlichen Bedingungen auftritt.

Beispiel: Stellen Sie sich vor, Sie haben zwei Threads, A und B.

In Thread A:

if( object.a != 0 )
    object.avg = total / object.a

In Thema B:

object.a = 0

Wenn Thread A ausgeschlossen wird, nachdem überprüft wurde, ob Objekt.a nicht null ist, wird B ausgeführt a = 0und wenn Thread A den Prozessor gewinnt, wird es eine "Division durch Null" machen.

Dieser Fehler tritt nur auf, wenn Thread A direkt nach der if-Anweisung vorweggenommen wird, es ist sehr selten, aber es kann passieren.


27
2017-08-29 16:03



Race Conditions treten in Multi-Thread-Anwendungen oder Multi-Prozess-Systemen auf. Eine Race Condition ist im Grunde genommen alles, was die Annahme voraussetzt, dass zwei Dinge, die nicht in demselben Thread oder Prozess sind, in einer bestimmten Reihenfolge passieren, ohne dafür zu sorgen, dass dies geschieht. Dies geschieht häufig, wenn zwei Threads Nachrichten übergeben, indem sie Mitgliedsvariablen einer Klasse festlegen und überprüfen, auf die beide zugreifen können. Es gibt fast immer eine Race Condition, wenn ein Thread Sleep aufruft, um einem anderen Thread Zeit zu geben, eine Task zu beenden (es sei denn, dieser Sleep befindet sich in einer Schleife, mit einem gewissen Prüfmechanismus).

Tools zum Verhindern von Race Conditions sind abhängig von der Sprache und dem Betriebssystem, aber einige sind Mutexe, kritische Abschnitte und Signale. Mutexe sind gut, wenn Sie sichergehen wollen, dass Sie als Einziger etwas tun. Signale sind gut, wenn Sie sicherstellen möchten, dass jemand anderes etwas getan hat. Die Minimierung von freigegebenen Ressourcen kann auch dazu beitragen, unerwartetes Verhalten zu verhindern

Rennen zu erkennen kann schwierig sein, aber es gibt ein paar Anzeichen. Ein Code, der stark auf "Sleep" basiert, ist anfällig für Race Conditions. Überprüfen Sie daher zunächst, ob Anrufe im betroffenen Code in den Ruhezustand versetzt werden. Das Hinzufügen von besonders langen Betten kann auch zum Debuggen verwendet werden, um eine bestimmte Reihenfolge von Ereignissen zu erzwingen. Dies kann nützlich sein, um das Verhalten zu reproduzieren, zu sehen, ob Sie es verschwinden lassen können, indem Sie das Timing der Dinge ändern, und um getestete Lösungen zu testen. Die Betten sollten nach dem Debuggen entfernt werden.

Das Unterschriftszeichen, dass man eine Race-Bedingung hat, ist jedoch, wenn ein Problem auftritt, das nur zeitweise auf einigen Maschinen auftritt. Häufige Bugs wären Abstürze und Deadlocks. Mit der Protokollierung sollten Sie den betroffenen Bereich finden und von dort aus arbeiten können.


18
2017-08-29 16:12



Race Condition bezieht sich nicht nur auf Software, sondern auch auf Hardware. Eigentlich wurde der Begriff zunächst von der Hardware-Industrie geprägt.

Gemäß Wikipedia:

Der Begriff stammt aus der Idee von zwei Signale rasen gegeneinander zu    beeinflussen Sie den Ausgang zuerst.

Race-Bedingung in einer logischen Schaltung:

enter image description here

Die Softwarebranche nahm diesen Begriff ohne Modifikation, was es ein wenig schwierig zu verstehen macht.

Sie müssen etwas ersetzen, um es der Softwarewelt zuzuordnen:

  • "zwei Signale" => "zwei Threads" / "zwei Prozesse"
  • "Beeinflusse die Ausgabe" => "Beeinflussung eines gemeinsamen Zustands"

Race Condition in der Softwareindustrie bedeutet also "zwei Threads" / "zwei Prozesse", die sich gegenseitig "beeinflussen", und das Endergebnis des Shared State hängt von einem subtilen Timing-Unterschied ab, der durch bestimmte Faktoren verursacht werden kann Thread- / Prozess-Startreihenfolge, Thread- / Prozessplanung usw.


9
2017-08-04 03:57



Microsoft hat tatsächlich eine wirklich detaillierte veröffentlicht Artikel in dieser Frage der Rassenbedingungen und Deadlocks. Die am meisten zusammengefasste Zusammenfassung davon wäre der Titelabsatz:

Eine Race-Bedingung tritt auf, wenn zwei Threads auf eine gemeinsame Variable zugreifen   die selbe Zeit. Der erste Thread liest die Variable und der zweite   Der Thread liest den gleichen Wert aus der Variablen. Dann der erste Thread   und der zweite Thread führt ihre Operationen mit dem Wert aus und sie rasen   um zu sehen, welcher Thread den letzten Wert für die gemeinsame Variable schreiben kann.   Der Wert des Threads, der seinen Wert zuletzt schreibt, wird beibehalten.   weil der Thread den Wert des vorherigen Threads überschreibt   schrieb.


7
2017-09-14 08:00



Eine Race-Bedingung ist eine Situation bei gleichzeitiger Programmierung, bei der zwei gleichzeitige Threads oder Prozesse und der resultierende Endzustand davon abhängen, wer die Ressource zuerst erhält.


4
2017-08-29 16:07