Was ist eine Rennbedingung?

982

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?

bmurphy1976
quelle
3
In der HOWTO zur sicheren Programmierung für Linux gibt es ein großartiges Kapitel, in dem beschrieben wird, was sie sind und wie sie vermieden werden können.
Craig H
4
Ich möchte erwähnen, dass - ohne Angabe der Sprache - die meisten Teile dieser Frage nicht richtig beantwortet werden können, da in verschiedenen Sprachen die Definition, die Konsequenzen und die Werkzeuge, um sie zu verhindern, unterschiedlich sein können.
MikeMB
@ MikeMB. Einverstanden, außer bei der Analyse der Bytecode-Ausführung, wie sie von Race Catcher durchgeführt wird (siehe diesen Thread stackoverflow.com/a/29361427/1363844 ), können wir alle ungefähr 62 Sprachen ansprechen, die zu Bytecode kompiliert werden (siehe en.wikipedia.org) / wiki / List_of_JVM_languages )
Ben

Antworten:

1238

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:

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 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:

// 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
Lehane
quelle
121
Was macht der andere Thread, wenn er auf die Sperre stößt? Wartet es? Error?
Brian Ortiz
174
Ja, der andere Thread muss warten, bis die Sperre aufgehoben wird, bevor er fortfahren kann. Dies macht es sehr wichtig, dass die Verriegelung durch den Haltefaden gelöst wird, wenn er damit fertig ist. Wenn es nie veröffentlicht wird, wartet der andere Thread auf unbestimmte Zeit.
Lehane
2
@Ian In einem Multithread-System gibt es immer Zeiten, in denen Ressourcen gemeinsam genutzt werden müssen. Zu sagen, dass ein Ansatz schlecht ist, ohne eine Alternative anzugeben, ist einfach nicht produktiv. Ich bin immer auf der Suche nach Verbesserungsmöglichkeiten und wenn es eine Alternative gibt, werde ich sie gerne erforschen und die Vor- und Nachteile abwägen.
Despertar
2
@Despertar ... es ist auch nicht unbedingt so, dass Ressourcen immer in einem Milti-Thread-System gemeinsam genutzt werden müssen. Beispielsweise könnten Sie ein Array haben, in dem jedes Element verarbeitet werden muss. Sie könnten möglicherweise das Array partitionieren und einen Thread für jede Partition haben, und die Threads können ihre Arbeit völlig unabhängig voneinander ausführen.
Ian Warburton
12
Damit ein Rennen stattfinden kann, reicht es aus, wenn ein einzelner Thread versucht, die gemeinsam genutzten Daten zu ändern, während der Rest der Threads sie entweder lesen oder ändern kann.
SomeWittyUsername
213

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:

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

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)

Rufen Sie den Wert von x ab
Addiere 1 zu diesem Wert
Speichern Sie diesen Wert in x

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:

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

Rennbedingungen können vermieden werden, indem vor dem Code, der auf die gemeinsam genutzte Ressource zugreift, eine Art Sperrmechanismus verwendet wird:

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

Hier lautet die Antwort jedes Mal 50.000.000.

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

Privatheit
quelle
Unter jakob.engbloms.se/archives/65 finden Sie ein Beispiel für ein Programm, mit dem Sie testen können, wie viele solche Probleme auftreten. Dies hängt wirklich vom Speichermodell des Computers ab, auf dem Sie ausgeführt werden.
Jakobengblom2
1
Wie kann es auf 50 Millionen kommen, wenn es bei 10 Millionen aufhören muss?
9
@nocomprende: Durch 5 Threads, die den gleichen Code gleichzeitig ausführen, wie direkt unter dem Snippet beschrieben ...
Jon Skeet
4
@ JonSkeet Du hast recht, ich habe das i und das x verwechselt. Vielen Dank.
Die doppelte Überprüfung der Sperre bei der Implementierung des Singleton-Musters ist ein Beispiel für die Verhinderung von Rennbedingungen.
Bharat Dodeja
150

Was ist eine Rennbedingung?

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.

Wie erkennt man sie?

Überprüfung des religiösen Codes, Multithread-Unit-Tests. Es gibt keine Verknüpfung. Es gibt nur wenige Eclipse-Plugins, aber noch nichts Stabiles.

Wie gehen Sie damit um und verhindern Sie es?

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 .

Vishal Shukla
quelle
Codeüberprüfungen und Komponententests sind zweitrangig, um den Fluss zwischen Ihren Ohren zu modellieren und den gemeinsam genutzten Speicher weniger zu nutzen.
Acumenus
2
Ich schätzte das reale Beispiel einer Rennbedingung
Tom O.
11
Wie die Antwort Daumen hoch . Die Lösung lautet: Sie sperren die Tickets zwischen 4 und 5 mit Mutex (gegenseitige Ausnahme, c ++). In der realen Welt heißt es Ticketreservierung :)
Volt
1
wäre eine anständige Antwort, wenn Sie die Nur-Java-Bits fallen lassen würden (die Frage bezieht sich nicht auf Java, sondern auf die Rennbedingungen im Allgemeinen)
Corey Goldberg
Nein, dies ist keine Rennbedingung. Aus "geschäftlicher" Sicht haben Sie einfach zu lange gewartet. Offensichtlich ist Rückstand keine Lösung. Versuchen Sie es mit einem Scalper, andernfalls kaufen Sie das Ticket einfach als Versicherung
csherriff
65

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.

Baris Kasikci
quelle
Der Unterschied ist entscheidend, um den Rennzustand zu verstehen. Vielen Dank!
ProgramCpp
37

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.

Chris Conway
quelle
3
Könnten Sie bitte ein Beispiel geben, wie nützlich die Rennbedingungen sein können? Googeln hat nicht geholfen.
Alex V.
3
@ Alex V. Zu diesem Zeitpunkt habe ich keine Ahnung, wovon ich sprach. Ich denke, dies mag ein Hinweis auf eine sperrfreie Programmierung gewesen sein, aber es ist nicht wirklich genau zu sagen, dass dies von den Rennbedingungen an sich abhängt.
Chris Conway
33

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:

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

In Thread B:

object.a = 0

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.

Steve Gury
quelle
21

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 :

Der Begriff stammt aus der Idee, dass zwei Signale gegeneinander antreten , um zuerst die Ausgabe zu beeinflussen .

Racebedingung in einer Logikschaltung:

Geben Sie hier die Bildbeschreibung ein

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:

  • "zwei Signale" => "zwei Threads" / "zwei Prozesse"
  • "Einfluss auf die Ausgabe" => "Einfluss auf einen gemeinsamen Zustand"

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.

Nybon
quelle
20

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.

Jorge Córdoba
quelle
nur brillante Erklärung
gokareless
Endzustand von was?
Roman Alexandrovich
1
@ RomanAlexandrovich Der endgültige Status des Programms. Der Zustand, der sich auf Dinge wie die Werte von Variablen usw. bezieht. Siehe Lehanes ausgezeichnete Antwort. Der "Zustand" in seinem Beispiel würde sich auf die Endwerte von 'x' und 'y' beziehen.
AMTerp
19

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.

Tsellon
quelle
10

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:

Eine Race-Bedingung tritt auf, wenn zwei Threads gleichzeitig auf eine gemeinsam genutzte Variable zugreifen. Der erste Thread liest die Variable und der zweite Thread liest den gleichen Wert aus der Variablen. Dann führen der erste Thread und der zweite Thread ihre Operationen für den Wert aus, und sie versuchen herauszufinden, welcher Thread den Wert zuletzt in die gemeinsam genutzte Variable schreiben kann. Der Wert des Threads, der seinen Wert zuletzt schreibt, bleibt erhalten, da der Thread über den Wert schreibt, den der vorherige Thread geschrieben hat.

Konstantin Dinev
quelle
5

Was ist eine Rennbedingung?

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.

Wie erkennt man sie?

Es gibt Tools, um den Rennzustand automatisch zu erkennen:

Wie gehst du damit um?

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.

Wie verhindern Sie das Auftreten?

Es gibt verschiedene Möglichkeiten, um Rennbedingungen zu verhindern, z. B. die Vermeidung kritischer Abschnitte .

  1. Keine zwei Prozesse gleichzeitig in ihren kritischen Bereichen. ( Gegenseitiger Ausschluss)
  2. Es werden keine Annahmen über die Geschwindigkeit oder die Anzahl der CPUs getroffen.
  3. Kein Prozess läuft außerhalb seines kritischen Bereichs, der andere Prozesse blockiert.
  4. Kein Prozess muss ewig warten, um in seinen kritischen Bereich zu gelangen. (A wartet auf B Ressourcen, B wartet auf C Ressourcen, C wartet auf A Ressourcen)
Adnan Qureshi
quelle
2

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.

Dilbag Koundal
quelle
2

Hier ist das klassische Beispiel für einen Kontostand, das Neulingen hilft, Threads in Java unter den Rennbedingungen leicht zu verstehen:

public class BankAccount {

/**
 * @param args
 */
int accountNumber;
double accountBalance;

public synchronized boolean Deposit(double amount){
    double newAccountBalance=0;
    if(amount<=0){
        return false;
    }
    else {
        newAccountBalance = accountBalance+amount;
        accountBalance=newAccountBalance;
        return true;
    }

}
public synchronized boolean Withdraw(double amount){
    double newAccountBalance=0;
    if(amount>accountBalance){
        return false;
    }
    else{
        newAccountBalance = accountBalance-amount;
        accountBalance=newAccountBalance;
        return true;
    }
}

public static void main(String[] args) {
    // TODO Auto-generated method stub
    BankAccount b = new BankAccount();
    b.accountBalance=2000;
    System.out.println(b.Withdraw(3000));

}
realPK
quelle
1

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:

AtomicInteger ai = new AtomicInteger(2);
ai.getAndAdd(5);

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!

Aleksei Moshkov
quelle
0

Versuchen Sie dieses grundlegende Beispiel, um den Zustand des Rennens besser zu verstehen:

    public class ThreadRaceCondition {

    /**
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        Account myAccount = new Account(22222222);

        // Expected deposit: 250
        for (int i = 0; i < 50; i++) {
            Transaction t = new Transaction(myAccount,
                    Transaction.TransactionType.DEPOSIT, 5.00);
            t.start();
        }

        // Expected withdrawal: 50
        for (int i = 0; i < 50; i++) {
            Transaction t = new Transaction(myAccount,
                    Transaction.TransactionType.WITHDRAW, 1.00);
            t.start();

        }

        // Temporary sleep to ensure all threads are completed. Don't use in
        // realworld :-)
        Thread.sleep(1000);
        // Expected account balance is 200
        System.out.println("Final Account Balance: "
                + myAccount.getAccountBalance());

    }

}

class Transaction extends Thread {

    public static enum TransactionType {
        DEPOSIT(1), WITHDRAW(2);

        private int value;

        private TransactionType(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }
    };

    private TransactionType transactionType;
    private Account account;
    private double amount;

    /*
     * If transactionType == 1, deposit else if transactionType == 2 withdraw
     */
    public Transaction(Account account, TransactionType transactionType,
            double amount) {
        this.transactionType = transactionType;
        this.account = account;
        this.amount = amount;
    }

    public void run() {
        switch (this.transactionType) {
        case DEPOSIT:
            deposit();
            printBalance();
            break;
        case WITHDRAW:
            withdraw();
            printBalance();
            break;
        default:
            System.out.println("NOT A VALID TRANSACTION");
        }
        ;
    }

    public void deposit() {
        this.account.deposit(this.amount);
    }

    public void withdraw() {
        this.account.withdraw(amount);
    }

    public void printBalance() {
        System.out.println(Thread.currentThread().getName()
                + " : TransactionType: " + this.transactionType + ", Amount: "
                + this.amount);
        System.out.println("Account Balance: "
                + this.account.getAccountBalance());
    }
}

class Account {
    private int accountNumber;
    private double accountBalance;

    public int getAccountNumber() {
        return accountNumber;
    }

    public double getAccountBalance() {
        return accountBalance;
    }

    public Account(int accountNumber) {
        this.accountNumber = accountNumber;
    }

    // If this method is not synchronized, you will see race condition on
    // Remove syncronized keyword to see race condition
    public synchronized boolean deposit(double amount) {
        if (amount < 0) {
            return false;
        } else {
            accountBalance = accountBalance + amount;
            return true;
        }
    }

    // If this method is not synchronized, you will see race condition on
    // Remove syncronized keyword to see race condition
    public synchronized boolean withdraw(double amount) {
        if (amount > accountBalance) {
            return false;
        } else {
            accountBalance = accountBalance - amount;
            return true;
        }
    }
}
Morsu
quelle
0

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 .

kiriloff
quelle
Auf welcher Sprache basiert Ihre Antwort?
MikeMB
Ehrlich gesagt scheint es mir , dass , wenn Sie Rennbedingungen haben per se , Sie sind nicht der Code in einer streng kontrollierten Weise Architecting. Dies ist zwar in Ihrem theoretischen Fall möglicherweise kein Problem, weist jedoch auf größere Probleme bei der Art und Weise hin, wie Sie Software entwerfen und entwickeln. Erwarten Sie früher oder später schmerzhafte Fehler im Rennzustand.
Ingenieur
0

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.

int i = 0;

Ausgabe

CounterThread -> i = 1  
DisplayThread -> i = 1  
CounterThread -> i = 2  
CounterThread -> i = 3  
CounterThread -> i = 4  
DisplayThread -> i = 4

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

Bharanitharan
quelle
0

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.

Hautausschläge
quelle