Golang: die Vermeidung von race-conditions
Was sind einige gute Praktiken zu verhindern, dass race-conditions in Gehen?
Die einzige, die ich denken kann, ist nicht Austausch von Daten zwischen goroutines die Eltern goroutine sendet eine Tiefe Kopie eines Objekts, nicht das Objekt selbst, so dass das Kind goroutine kann nicht mutieren, etwas, was die Eltern können. Dies würde mehr heap-Speicher, aber die andere alternative ist, zu lernen, Haskell 😛
Edit: außerdem, gibt es irgendein Szenario, in dem die Methode, die ich oben beschrieben immer noch in race conditions?
- Race conditions sind schwer zu Debuggen, ist es auch schwierig zu meistern, Sie zu vermeiden. Ich würde vorschlagen, denken über die Muster, die hier beschrieben von Rob Pike vimeo.com/49718712, wie er geht durch bestimmte Konstrukte wie Kanäle und die CSP-Semantik einer Anwendung sicher durch design anstatt sich Gedanken über alle Probleme, die im Zusammenhang mit mutexs. Tut mir Leid wenn das nicht Ihre Frage beantworten, aber ich hoffe, es öffnet mehr Türen zu neuen Ideen.
- Danke für den link. Yep, race conditions sind schwer zu verhindern, aber auch schwieriger zu Debuggen! Ich denke, eine unveränderliche version der Sammlungen lindern würde das problem in gewisser Weise, aber dann sind wir wieder in die funktionale Programmierung Hinterhof.
- Es gibt andere unveränderlich-basierte/funktionale Sprachen (wie z.B. F#), aber es ist wirklich style. Du hast Recht darüber brauchen mehr Speicher, aber die richtige Gestaltung können zu beseitigen einige der overhead. Zum Beispiel, schreiben-auf-die Modifikation von arrays kann man in
n log n
Raum (oder weniger für einige mögliche Fälle) im Durchschnitt eher als eine naive2n
. Obwohl Sprache/optimizer unterstützen können, tun einige verrückte Dinge, die für den Zweck gebauten Sprachen...
Du musst angemeldet sein, um einen Kommentar abzugeben.
Race conditions können sicherlich noch vorhanden sein, auch mit nicht-gemeinsamen Datenstrukturen. Betrachten Sie das folgende:
Diese "race" - Bedingung erfordert private mutable state in A, aber keine shared mutable Datenstrukturen und erfordert nicht einmal mutable state in B oder C. Es gibt nichts, B oder C tun können, um zu verhindern, dass diese "race" - Bedingung ohne das Verständnis des Vertrages, dass Eine bietet.
Sogar Haskell leiden kann, sind diese Arten von race conditions sobald der Staat tritt in der Gleichung, und der Zustand ist sehr schwer vollständig zu beseitigen, die von einem realen system. Schließlich wollen Sie Ihr Programm, um die Interaktion mit der Realität, und die Realität ist stateful. Wikipedia gibt eine hilfreiche race condition Beispiel in Haskell mit STM.
Ich bin damit einverstanden, dass gute unveränderliche Datenstrukturen konnte machen Dinge leichter (Gehen spielt nicht wirklich eine hast). Veränderliche Kopien der Handel ein problem für die anderen. Sie können nicht versehentlich ändern Daten von jemand anderem. Auf der anderen Seite, können Sie denke, dass man die änderung der real man, wenn man eigentlich nur die änderung einer Kopie, was zu einer anderen Art von Fehler. Sie haben, um zu verstehen, den Vertrag so oder so.
Aber letztlich Gehen Sie neigt zu Folgen, die Geschichte von C auf Parallelität: Sie machen einige ownership-Regeln für Ihren code (wie @tux21b bietet) und stellen Sie sicher, dass Sie immer Ihnen Folgen, und wenn Sie tun es perfekt, es werden alle große Arbeit, und wenn Sie jemals einen Fehler machen, dann ist es natürlich deine Schuld, nicht die Sprache.
(Versteh mich nicht falsch; ich mag Gehen, ganz viel wirklich. Und es bietet einige nette Werkzeuge, um die Parallelität einfach. Es funktioniert einfach nicht bieten viele Sprach-tools zu helfen, machen Parallelität richtige. Das ist bis zu Ihnen. Das heißt, tux21b Antwort bietet viele gute Ratschläge, und das Rennen Detektor ist definitiv ein mächtiges Werkzeug für die Reduzierung von race conditions. Es ist einfach nicht Teil der Sprache, und es ist zum testen, nicht auf Richtigkeit; Sie sind nicht das gleiche.)
EDIT: Auf die Frage, warum immutable Datenstrukturen Dinge viel einfacher machen, das ist die Verlängerung der Ausgangspunkt: die Erstellung eines Vertrages, in denen mehrere Parteien, die sich nicht ändern, die gleiche Daten-Struktur. Wenn die Daten-Struktur unveränderlich ist, dann kommt für frei...
Viele Sprachen haben eine reiche Reihe von unveränderlichen Sammlungen und Klassen. C++ können Sie
const
einfach über alles. Objective-C hat unveränderliche Sammlungen mit veränderlichen Unterklassen (erzeugt ein anderes Muster alsconst
). Scala hat getrennte veränderliche und unveränderliche Versionen viele collection-Typen, und es ist üblich, zu verwenden, die unveränderliche Versionen ausschließlich. Deklarieren Unveränderlichkeit in einer Methodensignatur ist ein wichtiger Hinweis auf den Vertrag.Wenn Sie an einem
[]byte
zu einer goroutine, es gibt keinen Weg, um zu wissen, den code, ob die goroutine beabsichtigt, ändern Sie die Scheibe, noch, wenn Sie möglicherweise ändern Sie die Scheibe selbst. Gibt es ein Muster, das aufstrebende, aber Sie sind wie ein C++ - Objekt, Besitz, bevor move-Semantik; viele schöne Ansätze, aber keine Möglichkeit zu wissen, was man im Einsatz. Es ist eine kritische Sache, dass jedes Programm benötigt zu tun korrekt, noch die Sprache, gibt Sie keine gute Werkzeuge, und es gibt keine Universelle Muster, die von Entwicklern genutzt.Gehen nicht durchsetzen Speicher Sicherheit statisch. Es gibt mehrere Möglichkeiten zu behandeln das problem, auch in großen code-Basen, die aber alle Ihre Aufmerksamkeit erfordern.
Senden Sie Zeiger herum, aber eine gemeinsame Sprache ist, um das signal der übertragung des Eigentums durch senden eines Zeigers. E. g., einmal übergeben Sie den Zeiger eines Objekts auf ein anderes Goroutine, Sie nicht berühren Sie es wieder, es sei denn, Sie erhalten das Objekt wieder aus, die goroutine (oder jede andere Goroutine, wenn das Objekt übergeben wird, um mehrere Male) durch ein anderes signal.
Wenn Ihre Daten von vielen Benutzern gemeinsam genutzt und ändert sich nicht, die oft, können Sie einen Zeiger auf die Daten weltweit und damit alle Lesen können. Wenn eine Goroutine ändern will, muss es sich um Folgen der copy-on-write-idiom, d.h. das Objekt kopieren, mutieren Sie die Daten, versuchen Sie, den Zeiger auf das neue Objekt, indem Sie mit so etwas wie atomic.CompareAndSwap.
Verwendung eines Mutex (oder ein RWMutex wenn Sie zulassen möchten, dass viele gleichzeitige Leser auf einmal) gar nicht so schlecht. Sicher, ein Mutex ist keine silberne Kugel, und es ist oft nicht eine gute Passform für die tun, die Synchronisierung (und seine überstrapaziert in vielen Sprachen, die führen zu dem schlechten Ruf), aber manchmal ist es die einfachste und effizienteste Lösung.
Wahrscheinlich gibt es viele andere Möglichkeiten. Senden Werte nur durch kopieren ist ein weiteres und einfach zu überprüfen, aber ich denke, Sie sollten nicht beschränken Sie sich auf diese Methode nur. Wir sind alle reif, und wir sind alle in der Lage sind, Dokumentation zu Lesen (vorausgesetzt, Sie dokumentieren Sie Ihren code richtig).
Den Go-tool kommt auch mit einer sehr wertvollen race-Detektor gebaut, das in der Lage ist, zu erkennen, Rassen zur Laufzeit. Schreiben Sie eine Menge von tests, und führen Sie Sie mit dem Rennen Detektor aktiviert und nehmen jede Fehlermeldung ernst. Sie in der Regel deuten auf eine schlechte oder komplizierte Konstruktion.
(PS: möchten Sie vielleicht einen Blick auf Rost wenn Sie möchten, eine compiler-und Typ-system, das in der Lage ist, zu überprüfen, gleichzeitigen Zugriff während der compile-Zeit, während immer noch eine shared state. Ich habe nicht verwendet es selbst, aber die Ideen sehen sehr vielversprechend aus.)