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?
Antworten:
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.
quelle
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.
quelle
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.
quelle
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 ...
quelle
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.
quelle
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.
quelle
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.
quelle
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.
quelle
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.
quelle
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.
quelle
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.
quelle
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 :)
quelle
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.
quelle
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.
quelle
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.
quelle
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.
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.quelle