Sollte ich auf Rennbedingungen achten, die mit ziemlicher Sicherheit nicht eintreten können?


52

Betrachten wir eine GUI-Anwendung, bei der der Hauptthread die Benutzeroberfläche fast augenblicklich aktualisiert und ein anderer Thread Daten über das Netzwerk abruft oder es garantiert 5-10 Sekunden dauert, bis der Auftrag abgeschlossen ist.

Ich habe viele verschiedene Antworten dafür erhalten, aber einige Leute sagen, wenn es sich um eine Rassenbedingung handelt, die statistisch unmöglich ist, machen Sie sich darüber überhaupt keine Sorgen, aber andere sagen, wenn es überhaupt 10 - 53 % gibt (ich mache Spaß) Wenn Sie sich nicht an den Zahlen orientieren, wie ich gehört habe, ist aufgrund der Rennbedingungen etwas Voodoo-Magie passiert. Lassen Sie die Sperren für den Thread, der sie benötigt, immer los.

Was sind deine Gedanken? Ist es eine gute Programmierpraxis, mit Rennbedingungen in solchen statistisch unmöglichen Situationen umzugehen? oder wäre es völlig unnötig oder sogar kontraproduktiv, mehr Codezeilen hinzuzufügen, um die Lesbarkeit zu beeinträchtigen?


21
Wenn die Leute solche Chancen angeben, warum fragt dann niemand nach der Ausbildung der Person, die diese Zahl angibt? Sie benötigen eine formelle Ausbildung in Statistik, bevor Sie eine solche Zahl verwenden können.
Pieter B

27
Als Physiker bedeutet p <1E-140 p = 0. Wird in diesem Universum nicht passieren. 0.000000000000000000000000000000000000000000000000001% ist viel größer.
MSalters

15
Stellen Sie sicher, dass diese Racebedingung nicht dazu führen kann, dass jemand Ihre App absichtlich abstürzt. Dies könnte die Ursache eines Sicherheitsproblems sein.
Toasted_flakes

27
Eine von einer Million Chancen ist neun Mal von zehn.
Kaz Dragon

27
"Hat es mit ziemlicher Sicherheit keine Chance?" Das bedeutet, dass es um 3 Uhr morgens in der Produktion stattfindet und höchstwahrscheinlich sehr teuer ist.

Antworten:


137

Wenn es sich wirklich um ein 1 in 10 ^ 55-Ereignis handelt, muss kein Code dafür erstellt werden. Das würde bedeuten, dass, wenn Sie die Operation 1 Million Mal pro Sekunde durchführen würden, alle 3 * 10 ^ 41 Jahre ein Fehler auftritt, was ungefähr dem 10 ^ 31-fachen des Zeitalters des Universums entspricht. Wenn Ihre Anwendung nur einmal in jeder Billion Billion Milliarden Alter des Universums einen Fehler aufweist, ist dies wahrscheinlich zuverlässig genug.

Allerdings würde ich sehr stark wetten, dass der Fehler bei weitem nicht so unwahrscheinlich ist. Wenn Sie sich den Fehler vorstellen können, ist es fast sicher, dass er zumindest gelegentlich auftritt, sodass es sich zunächst lohnt, ihn richtig zu codieren. Wenn Sie die Threads zu Beginn richtig codieren, damit sie Sperren erhalten und in geeigneter Weise aufheben, kann der Code in Zukunft viel besser verwaltet werden. Sie müssen sich keine Sorgen machen, wenn Sie eine Änderung vornehmen, dass Sie alle potenziellen Rennbedingungen neu analysieren, ihre Wahrscheinlichkeiten neu berechnen und sicherstellen müssen, dass sie nicht wiederkehren.


66
Ich erinnere mich an einen Kommentar, den ich vor Jahren gelesen habe, kann ihn aber jetzt nicht finden. +1 dafür, dass es "bei weitem nicht so unwahrscheinlich" ist.
Bevan

2
+1 für den Einsatz. Der beste Weg, mit den Rennbedingungen umzugehen, ist, sie loszuwerden.
Blrfl

10
@Bevan "Eine 1 in einer Million Chance ist normalerweise nächsten Dienstag" ... es sei denn, Sie spielen eine Lotterie :)
dasblinkenlight

22
@dasblinkenlight Aber die Chancen von jemandem in den meisten Lotterien gewinnen 100% nähert. Vorhersagen, wer , das ist jetzt die Herausforderung.
Bevan

3
@Bevan: Genau dieser Kommentar ging mir durch den Kopf, als ich die Frage las - hier ist die Referenz: blogs.msdn.com/b/larryosterman/archive/2004/03/30/104165.aspx
Doc Brown

69

Vom Kosten-Nutzen-Standpunkt aus sollten Sie zusätzlichen Code nur dann schreiben, wenn Sie genügend Nutzen daraus ziehen.

Wenn zum Beispiel das Schlimmste, was passieren würde, wenn ein falscher Thread "das Rennen gewinnt", ist, dass die Informationen nicht angezeigt werden und der Benutzer auf "Aktualisieren" klicken müsste Viel Code zu schreiben ist es nicht wert, etwas so Bedeutungsloses zu reparieren.

Wenn die Racebedingung andererseits zu falschen Geldtransfers zwischen Bankkonten führen kann, müssen Sie sich vor Racebedingung schützen, unabhängig davon, wie viel Code Sie schreiben müssen, um dieses Problem zu lösen.


20
+1: Zur Unterscheidung zwischen "Misserfolg, der nach Misserfolg aussieht" und "Misserfolg, der nach Erfolg aussieht". Falsche Informationen sind je nach Domäne viel schwerwiegender.
Deworde

2
+1 Es macht einen großen Unterschied, was die Ergebnisse der Rennbedingungen sein könnten.
Grant

+1 Die Konsequenz der Rennbedingung sollte ein entscheidender Faktor sein, wenn sie angegangen werden soll. Eine Racebedingung, die einen Flugzeugabsturz verursachen kann, unterscheidet sich stark von einer Bedingung, die den Benutzer zwingen kann, eine Anwendung erneut zu öffnen.
Stöbern Sie

1
+1: Ich würde sagen, dass die Konsequenzen wahrscheinlich die sind, die Sie analysieren sollten, und nicht die Wahrscheinlichkeit, dass sie auftreten. Wenn die Konsequenzen keine Rolle spielen, müssen Sie möglicherweise AUCH nicht mit den Rennbedingungen umgehen, wenn sie sehr häufig sind.
Leo

1
Aber nehmen Sie nicht an, dass das automatische Beheben einer Racebedingung bedeutet, dass Sie mehr Code schreiben müssen. Es könnte genauso gut bedeuten, einen großen Teil des fehlerhaften Codes zu entfernen und ihn durch einen kleineren Teil des korrekten Codes zu ersetzen.
JesperE

45

Das Finden einer Rennbedingung ist der schwierige Teil. Sie haben wahrscheinlich fast so viel Zeit damit verbracht, diese Frage zu schreiben, wie Sie gebraucht hätten, um sie zu beheben. Es ist nicht so, dass es so viel weniger lesbar wäre. Programmierer erwarten , dass in solchen Situationen Synchronisationscode angezeigt wird, und sie werden möglicherweise mehr Zeit damit verschwenden, sich zu fragen, warum dieser nicht vorhanden ist und ob das Hinzufügen dieses Codes ihren nicht verwandten Fehler beheben würde.

Wenn es um Wahrscheinlichkeiten geht, wären Sie überrascht. Ich hatte letztes Jahr einen Fehlerbericht über den Rennzustand, den ich nicht mit Tausenden von automatisierten Versuchen reproduzieren konnte, aber ein System eines Kunden hat ihn die ganze Zeit gesehen. Der geschäftliche Wert von 5 Minuten, um es jetzt zu beheben, im Vergleich zur möglichen Behebung eines "unmöglichen" Fehlers bei der Installation eines Kunden, macht die Wahl zu einem Kinderspiel.


1
Das auch! Vermeiden Sie, dass andere Programmierer über mögliche Probleme beim Lesen Ihres Codes nachdenken, indem Sie das tun, was erforderlich ist (auch wenn es "unwahrscheinlich" ist, dass dies fehlschlägt).
Casey Kuball

Ihr Punkt ist gut angenommen (Korrekturen, die jetzt vorgenommen werden, sind schneller und billiger als die später vorgenommenen), außer dass es niemals nur "5 Minuten dauern wird, um es jetzt zu beheben".
Bilderstürmer

2
+1 für den Hinweis auf , dass die Wahrscheinlichkeit , dass der Race - Bedingung hängt wahrscheinlich von vielen Faktoren ab , so auch wenn es in unwahrscheinlich sieht Ihre Konfiguration ist es häufiger auf einem Kundensystem / auf einem anderen OS / in usw. nächsten Release passieren kann
sleske

27

Besorgen und lösen Sie die Schlösser. Wahrscheinlichkeiten ändern sich, Algorithmen ändern sich. Es ist eine schlechte Angewohnheit, sich darauf einzulassen, und wenn etwas schief geht, müssen Sie nicht aufhören und sich fragen, ob Sie die falschen Chancen haben ...


6
+1 für Algorithmen ändern. Wenn Sie sich der Rennbedingungen bewusst sind, sind die Wahrscheinlichkeiten derzeit gering. Wenn Sie nach einem Jahr die Rennbedingungen vergessen haben, können Sie Ihren Code ändern, wodurch sich das Timing und die Wahrscheinlichkeit eines Fehlers erheblich ändern.
Phil

13

und ein anderer Thread fragt Daten über das Netzwerk ab oder es dauert garantiert 5-10 Sekunden, um den Auftrag abzuschließen.

Bis jemand eine Caching-Ebene einführt, um die Leistung zu verbessern. Plötzlich endete das andere Profil fast augenblicklich und der Rennzustand manifestierte sich mehr als oft.

Hätte genau dies vor ein paar Wochen passiert, dauerte es ungefähr 2 volle Entwicklertage, um den Fehler zu finden.

Repariere immer die Rennbedingungen, wenn du sie erkennst.


8

Einfach gegen richtig.

In vielen Fällen übertrifft die Einfachheit die Richtigkeit. Es ist ein Kostenproblem.

Auch Rennbedingungen sind unangenehme Dinge, die sich nicht an einfache Statistiken halten. Alles läuft gut, bis eine andere scheinbar unabhängige Synchronisation dazu führt, dass Ihr Rennzustand plötzlich zur Hälfte eintritt. Es sei denn, Sie schalten die Protokolle ein oder debuggen den Code.

Eine pragmatische Alternative zur Verhinderung einer Race-Bedingung (die schwierig sein kann) kann sein, sie zu erkennen und zu protokollieren (Bonus für hartes und frühes Versagen). Wenn es nie passiert, haben Sie wenig verloren. Wenn es tatsächlich passiert, haben Sie eine solide Rechtfertigung, die zusätzliche Zeit für die Behebung zu verwenden.


1
+1 für die Protokollierung und frühes Fehlschlagen, wenn die vollständige Behebung zu kompliziert ist.
Martin Ba

In vielen Fällen übertrifft die Einfachheit die Vollständigkeit. Synchronisation ist fast nie unter diesen Fällen. Es wird fast immer wiederkommen, um Sie (oder den armen Kerl, der mit der Pflege Ihres Codes beauftragt ist) später zu beißen.
Reirab

@ Reirab Ich bin anderer Meinung. Wenn Sie seltene Ereignisse berücksichtigen, ist ein protokollierter Fehler kostengünstig. Ein Beispiel: Wenn Ihre Telefon-App eine 1: 100-Fehlerrate (Absturz) aufweist, wechselt der Benutzer das Netzwerk zu einem bestimmten Zeitpunkt (1: 31: 23: 59: 00 -> 2: 1: 00: 00: 00) werde wahrscheinlich nie davon hören. Aber dann ist eine 1/10 ^ 9-Wahrscheinlichkeit eines Absturzes bei einer Verbindung auf einem Server nicht akzeptabel. Es hängt davon ab, ob.
Ptyx

7

Wenn Ihre Racebedingung sicherheitsrelevant ist, sollten Sie immer Code verwenden, um dies zu verhindern.

Ein häufiges Beispiel sind Race-Bedingungen beim Erstellen / Öffnen von Dateien unter Unix, die unter Umständen zu Eskalationsangriffen führen können, wenn das Programm mit der Race-Bedingung mit höheren Berechtigungen ausgeführt wird als der Benutzer, der damit interagiert, z. B. ein System-Daemon-Prozess oder Schlimmer noch, der Kernel.

Selbst wenn eine Racebedingung eine zufällige Wahrscheinlichkeit von 10 ^ (- 80) hat , kann es durchaus vorkommen, dass ein entschlossener Angreifer eine angemessene Chance hat, solche Bedingungen absichtlich und künstlich zu schaffen.


6

Therac-25!

Die Entwickler des Therac-25-Projekts waren ziemlich zuversichtlich, was das Timing zwischen einer Benutzeroberfläche und einem Schnittstellenproblem in einem therapeutischen XRAY-Gerät angeht.

Sie sollten nicht gewesen sein.

Weitere Informationen zu dieser berühmten Software-Katastrophe auf Leben und Tod finden Sie unter:

http://www.youtube.com/watch?v=izGSOsAGIVQ

oder

http://en.wikipedia.org/wiki/Therac-25

Ihre Anwendung ist möglicherweise weniger störanfällig als medizinische Geräte. Eine hilfreiche Methode besteht darin, das Risikoengagement als das Produkt der Eintrittswahrscheinlichkeit und der Eintrittskosten über die Lebensdauer des Produkts für alle Einheiten zu bewerten, die produziert werden könnten.

Wenn Sie sich dafür entschieden haben, Ihren Code für die Ewigkeit zu erstellen (und das hört sich so an, als ob Sie es getan hätten), sollten Sie das Moore-Gesetz in Betracht ziehen, das alle paar Jahre leicht mehrere Nullen abschaffen kann, wenn Computer innerhalb oder außerhalb Ihres Systems schneller werden. Wenn Sie Tausende von Kopien versenden, entfernen Sie weitere Nullen. Wenn Benutzer diesen Vorgang jahrelang täglich (oder monatlich) ausführen, nehmen Sie ein paar mehr mit. Wenn es verwendet wird, wo Google Fiber verfügbar ist, was dann? Beeinträchtigt das Rennen, wenn der Benutzeroberflächenmüll einen mittleren GUI-Betrieb erfasst? Verwenden Sie eine Open Source- oder Windows-Bibliothek hinter Ihrer GUI? Können Updates dort das Timing beeinflussen?

Semaphoren, Sperren, Mutexe und Barrier-Synchronisation gehören zu den Möglichkeiten, um Aktivitäten zwischen Threads zu synchronisieren. Wenn Sie sie nicht verwenden, kann sich möglicherweise eine andere Person, die Ihr Programm verwaltet, und dann ziemlich schnell die Annahme über die Beziehungen zwischen Threads verschieben, und die Berechnung über die Racebedingung wird möglicherweise ungültig.

Ich empfehle, dass Sie explizit synchronisieren, da ein Kunde möglicherweise ein Problem hat, obwohl Sie es nie bemerken. Auch wenn Ihr Rennzustand niemals eintritt, was ist, wenn Sie oder Ihre Organisation vor Gericht gestellt werden, um Ihren Code zu verteidigen (da Toyota vor einigen Jahren mit dem Prius verwandt war). Je gründlicher Ihre Methodik ist, desto besser werden Sie abschneiden. Es ist vielleicht besser zu sagen, "wir schützen uns vor diesem unwahrscheinlichen Fall wie diesem ..." als zu sagen, "wir wissen, dass unser Code scheitern wird, aber wir haben diese Gleichung aufgeschrieben, um zu zeigen, dass es in unserem Leben nicht passieren wird. Wahrscheinlich. "

Es klingt so, als ob die Wahrscheinlichkeitsberechnung von jemand anderem stammt. Kennen sie Ihren Code und kennen Sie sie genug, um darauf zu vertrauen, dass kein Fehler gemacht wurde? Wenn ich für etwas eine Zuverlässigkeit von 99,99997% errechnete, könnte ich auch an meine Statistikkurse am College zurückdenken und mich daran erinnern, dass ich nicht immer 100% erreicht habe und mich bei meinen persönlichen Zuverlässigkeitsschätzungen um einige Prozent verschlechtert habe.


1
+1 für die Erwähnung von Therac-25. Viele wichtige Lektionen hier.
Stuart Marks

Obwohl ich dies für eine gute Antwort halte, könnten Sie argumentieren, dass Ihr Hobby-GUI-Projekt mit Sicherheit nicht dazu führen wird, dass Menschen sterben, wenn Sie eine Race-Bedingung nicht beseitigen.
Marktani

Ich bin nicht sehr umstritten, aber wenn ich so wäre, könnte ich argumentieren, dass wir jedes Mal, wenn wir Code schreiben, ihn richtig schreiben sollten. Wenn wir es üben können, die Rennbedingungen aus unseren Hobbyprojekten herauszuholen, in denen der Code einfacher ist und wir vielleicht der einzige Autor sind, sind wir umso besser vorbereitet, wenn wir Arbeitsprojekte angehen, in denen die Arbeit mehrerer Autoren zusammengeführt werden muss.
DeveloperDon

4

Wäre es völlig unnötig oder sogar kontraproduktiv, mehr Codezeilen hinzuzufügen, um die Lesbarkeit zu beeinträchtigen?

Einfachheit ist nur dann gut, wenn sie auch stimmt. Da dieser Code nicht korrekt ist, werden zukünftige Programmierer ihn unweigerlich prüfen, wenn sie nach einem verwandten Fehler suchen.

Unabhängig davon, wie Sie damit umgehen (entweder durch Protokollieren, Dokumentieren oder Hinzufügen der Sperren - dies hängt von den Kosten ab), sparen Sie anderen Programmierern Zeit, wenn Sie sich den Code ansehen.


3

Dies würde vom Kontext abhängen. Wenn es ein lässiges iPhone-Spiel ist, wahrscheinlich nicht. Wahrscheinlich das Flugsteuerungssystem für das nächste bemannte Raumfahrzeug. Es hängt alles davon ab, welche Konsequenzen es hat, wenn das "schlechte" Ergebnis gemessen an den geschätzten Kosten für die Behebung auftritt.

Es gibt selten eine einheitliche Antwort für diese Art von Fragen, da es sich nicht um Programmierfragen handelt, sondern um wirtschaftliche Fragen.


3
"Das Flugsteuerungssystem für das nächste bemannte Raumfahrzeug" ENDGÜLTIG .
Deworde

wahrscheinlich ... auf jeden Fall ... es würde davon abhängen, wer in der Rakete war :-)
GroßmeisterB

3

Ja, erwarte das Unerwartete. Ich habe Stunden (im Code anderer Leute ^^) damit verbracht, Bedingungen aufzuspüren, die niemals eintreten sollten.

Dinge wie immer ein anderes haben, immer eine Standardeinstellung für Groß- und Kleinschreibung haben, Variablen initialisieren (ja, wirklich ... Fehler treten hierdurch auf), Ihre Schleifen auf wiederverwendete Variablen für jede Iteration überprüfen usw.

Lesen Sie Blogs, Artikel und Bücher zu diesem Thema, wenn Sie Bedenken haben, bestimmte Themen zu behandeln. Das aktuelle Thema scheint unveränderliche Daten zu sein.


3

Repariere es einfach.

Ich habe genau das gesehen. Ein Thread kann eine Netzwerkanforderung an einen Server senden, der eine komplexe Datenbanksuche durchführt und antwortet, bevor der andere Thread zur nächsten Codezeile gelangt. Es passiert.

Einige Kunden irgendwo werden eines Tages entscheiden, etwas auszuführen, das die gesamte CPU-Zeit für den "schnellen" Thread beansprucht, während der langsame Thread ausgeführt wird, und es wird Ihnen leid tun :)


1

Wenn Sie einen unwahrscheinlichen Rennzustand erkannt haben, dokumentieren Sie ihn zumindest im Code!

EDIT: Ich sollte hinzufügen, dass ich es beheben würde, wenn es überhaupt möglich ist, aber zum Zeitpunkt des Schreibens der obigen keine andere Antwort explizit sagte, zumindest dokumentieren Sie das Problem im Code.


1
Ja, und zumindest versuchen Sie es zu erkennen und zu protokollieren, wenn es passiert. IMHO ist es vollkommen in Ordnung, nicht jeden Fehler zu vermeiden. Aber lassen Sie wenigstens jemanden wissen, dass es passiert ist und dass Ihre Annahme, dass es nicht fehlgeleitet wurde.
Steve Bennett

0

Ich denke, wenn du bereits weißt, wie und warum es passieren könnte, könntest du genauso gut damit umgehen. Das ist, wenn es nicht viel Ressourcen verbraucht.


0

Es hängt alles davon ab, was die Konsequenzen eines Rennzustands sind. Ich denke, die Leute, die Ihre Frage beantworten, sind für ihre Arbeit richtig. Meins ist Router-Konfigurations-Engines. Für mich machen die Rennbedingungen die Systeme entweder still, korrupt oder unkonfiguriert, obwohl sie als erfolgreich eingestuft wurden. Ich verwende immer Semaphore pro Router, damit ich nichts von Hand aufräumen muss.

Ich denke, ein Teil meines GUI-Codes ist immer noch anfällig für Rennbedingungen, so dass einem Benutzer möglicherweise ein Fehler angezeigt wird, weil eine Rennbedingung aufgetreten ist, aber ich hätte keine derartigen Möglichkeiten, wenn die Wahrscheinlichkeit einer Datenverfälschung oder eines Fehlverhaltens des besteht Anwendung nach einem solchen Ereignis.


0

Lustigerweise bin ich vor kurzem auf dieses Problem gestoßen. Ich wusste nicht einmal, dass unter meinen Umständen eine Rennsituation möglich ist. Der Rennzustand zeigte sich erst, als Multi-Core-Prozessoren zur Norm wurden.

Das Szenario war ungefähr so. Ein Gerätetreiber hat Ereignisse ausgelöst, die von der Software verarbeitet werden sollen. Die Steuerung musste so schnell wie möglich zum Gerätetreiber zurückkehren, um eine Zeitüberschreitung auf dem Gerät zu verhindern. Um dies sicherzustellen, wurde das Ereignis in einem separaten Thread aufgezeichnet und in eine Warteschlange gestellt.

Receive event from device:
{
    Record event details.
    Enqueue event in the queuing thread.
    Acknowledge the event.
}

Queueing thread receives an event:
{
    Retrieve event details.
    Process event.
    Send next command to device.
}

Das hat jahrelang gut funktioniert. Dann würde es in bestimmten Konfigurationen plötzlich scheitern. Es stellte sich heraus, dass der Warteschlangenthread nun parallel zum Ereignisbehandlungsthread ausgeführt wurde, anstatt die Zeit eines einzelnen Prozessors gemeinsam zu nutzen. Es gelang, den nächsten Befehl an das Gerät zu senden, bevor das Ereignis bestätigt wurde, was zu einem Fehler außerhalb der Reihenfolge führte.

Da es nur einen Kunden in einer Konfiguration betraf, habe ich schändlicherweise einen eingetragen, Thread.Sleep(1000)bei dem das Problem auftrat. Es gab seitdem kein Problem mehr.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.