Ich bin ein ziemlich guter Programmierer, mein Chef ist auch ein ziemlich guter Programmierer. Obwohl er einige Aufgaben wie Multithreading zu unterschätzen scheint und wie schwierig es sein kann (ich finde es sehr schwierig, mehr als ein paar Threads auszuführen, darauf zu warten, dass alle fertig sind und dann Ergebnisse zurückgeben).
In dem Moment, in dem Sie sich Sorgen über Deadlocks und Rennbedingungen machen müssen, fällt es mir sehr schwer, aber der Chef scheint das nicht zu schätzen - ich glaube, er hat das noch nie erlebt. Einfach ein Schloss draufstecken ist so ziemlich die Einstellung.
Wie kann ich ihn also vorstellen oder erklären, warum er die Komplexität von Parallelität, Parallelität und Multithreading möglicherweise unterschätzt? Oder vielleicht irre ich mich?
Bearbeiten: Nur ein wenig von dem, was er getan hat - durchlaufen Sie eine Liste, und erstellen Sie für jedes Element in dieser Liste einen Thread, der einen Datenbankaktualisierungsbefehl basierend auf den Informationen in diesem Element ausführt. Ich bin mir nicht sicher, wie er gesteuert hat, wie viele Threads gleichzeitig ausgeführt wurden. Ich denke, er muss sie zu einer Warteschlange hinzugefügt haben, wenn zu viele ausgeführt wurden (er hätte kein Semaphor verwendet).
quelle
Antworten:
Wenn Sie sich auf mathematische Erfahrungen verlassen können, veranschaulichen Sie, wie ein normaler Ausführungsfluss, der im Wesentlichen deterministisch ist, nicht nur nicht deterministisch mit mehreren Threads, sondern auch exponentiell komplex wird, da Sie sicherstellen müssen, dass jede mögliche Verschachtelung von Maschinenanweisungen immer noch das Richtige bewirkt. Ein einfaches Beispiel für ein verlorenes Update oder eine Situation mit unsauberen Lesevorgängen ist oft ein Augenöffner.
"Slap a lock on it" ist die triviale Lösung ... sie löst alle Ihre Probleme, wenn Sie sich nicht um die Leistung sorgen. Versuchen Sie zu veranschaulichen, wie groß der Leistungseinbruch wäre, wenn beispielsweise Amazon die gesamte Ostküste sperren müsste, wenn jemand in Atlanta ein Buch bestellt!
quelle
Multithreading ist einfach. Das Codieren einer Anwendung für Multithreading ist sehr, sehr einfach.
Es gibt einen einfachen Trick, und dies ist eine gut gestaltete Nachrichtenwarteschlange Verwendung ( nicht Ihre eigene Rolle) Daten zwischen Threads zu übergeben.
Das Schwierige ist, dass mehrere Threads ein freigegebenes Objekt auf magische Weise aktualisieren. Dann wird es fehleranfällig, weil die Leute nicht auf die Rennbedingungen achten, die vorhanden sind.
Viele Leute verwenden keine Nachrichtenwarteschlangen und versuchen, gemeinsam genutzte Objekte zu aktualisieren und Probleme für sich selbst zu schaffen.
Schwierig wird es, einen Algorithmus zu entwerfen, der gut funktioniert, wenn Daten zwischen mehreren Warteschlangen ausgetauscht werden. Das ist schwierig. Die Funktionsweise von nebeneinander vorhandenen Threads (über gemeinsam genutzte Warteschlangen) ist jedoch einfach.
Beachten Sie auch, dass Threads teilen I / O - Ressourcen. Es ist unwahrscheinlich, dass ein E / A-gebundenes Programm (dh Netzwerkverbindungen, Dateivorgänge oder Datenbankvorgänge) mit vielen Threads schneller läuft.
Wenn Sie das Problem der Aktualisierung gemeinsam genutzter Objekte veranschaulichen möchten, ist dies ganz einfach. Setzen Sie sich mit ein paar Papierkarten über den Tisch. Schreiben Sie eine einfache Reihe von Berechnungen auf - 4 oder 6 einfache Formeln - mit viel Platz auf der Seite.
Hier ist das Spiel. Sie lesen jeweils eine Formel, schreiben eine Antwort und legen eine Karte mit der Antwort ab.
Jeder von euch wird die halbe Arbeit machen, oder? Du bist in der Hälfte der Zeit fertig, oder?
Wenn Ihr Chef nicht viel nachdenkt und gerade erst anfängt, kommt es zu Konflikten, und beide schreiben Antworten auf dieselbe Formel. Das hat nicht geklappt, weil es eine inhärente Rassenbedingung zwischen Ihnen beiden gibt, die vor dem Schreiben lesen. Nichts hindert Sie daran, die gleiche Formel zu lesen und die Antworten des jeweils anderen zu überschreiben.
Es gibt viele, viele Möglichkeiten, Rennbedingungen mit schlecht oder nicht gesperrten Ressourcen zu schaffen.
Wenn Sie alle Konflikte vermeiden möchten, schneiden Sie das Papier in einen Stapel Formeln. Sie nehmen einen aus der Warteschlange, schreiben die Antwort auf und veröffentlichen die Antworten. Keine Konflikte, da Sie beide aus einer Nur-Lese-Nachrichtenwarteschlange lesen.
quelle
Multithread-Programmierung ist wahrscheinlich die schwierigste Lösung für die Parallelität. Es ist im Grunde genommen eine ziemlich einfache Abstraktion dessen, was die Maschine tatsächlich tut.
Es gibt eine Reihe von Ansätzen, wie das Akteurmodell oder das (Software-) Transaktionsgedächtnis , die viel einfacher sind. Oder mit unveränderlichen Datenstrukturen (wie Listen und Bäumen) arbeiten.
Im Allgemeinen erleichtert eine ordnungsgemäße Trennung von Bedenken das Multithreading. Etwas, das allzu oft vergessen wird, wenn Leute 20 Threads erzeugen und alle versuchen, den gleichen Puffer zu verarbeiten. Verwenden Sie Reaktoren, bei denen Sie eine Synchronisierung benötigen, und übergeben Sie im Allgemeinen Daten zwischen verschiedenen Mitarbeitern mit Nachrichtenwarteschlangen.
Wenn Sie eine Sperre in Ihrer Anwendungslogik haben, haben Sie etwas falsch gemacht.
Technisch gesehen ist Multithreading also schwierig.
"Slap a lock on it" ist so gut wie die am wenigsten skalierbare Lösung für Parallelitätsprobleme und macht den ganzen Zweck des Multithreading zunichte. Damit wird ein Problem auf ein nicht gleichzeitiges Ausführungsmodell zurückgesetzt. Je mehr Sie dies tun, desto wahrscheinlicher ist es, dass jeweils nur ein Thread ausgeführt wird (oder 0 in einem Deadlock). Es macht den ganzen Zweck zunichte.
Das ist so, als würde man sagen: "Die Probleme der 3. Welt zu lösen ist einfach. Wirf einfach eine Bombe darauf." Nur weil es eine triviale Lösung gibt, ist das Problem nicht trivial, da Sie auf die Qualität des Ergebnisses achten.
In der Praxis ist die Lösung dieser Probleme genauso schwierig wie alle anderen Programmierprobleme und wird am besten mit geeigneten Abstraktionen durchgeführt. Das macht es eigentlich ganz einfach.
quelle
Ich denke, diese Frage hat einen nicht-technischen Aspekt - IMO geht es um Vertrauen. Wir werden häufig gebeten, komplexe Apps wie zum Beispiel Facebook zu reproduzieren. Ich bin zu dem Schluss gekommen, dass, wenn Sie dem Uneingeweihten / Management die Komplexität einer Aufgabe erklären müssen, in Dänemark etwas faul ist.
Selbst wenn andere Ninja-Programmierer die Aufgabe in 5 Minuten erledigen könnten, basieren Ihre Schätzungen auf Ihren persönlichen Fähigkeiten. Ihr Gesprächspartner sollte entweder lernen, Ihrer Meinung in dieser Angelegenheit zu vertrauen, oder jemanden einstellen, dessen Wort er zu akzeptieren bereit ist.
Die Herausforderung besteht nicht darin, die technischen Implikationen weiterzugeben, die die Menschen entweder eher ignorieren oder im Gespräch nicht erfassen können, sondern eine Beziehung des gegenseitigen Respekts aufzubauen.
quelle
Ein einfaches Gedankenexperiment zum Verständnis von Deadlocks ist das Problem des " Essensphilosophen ". Eines der Beispiele, mit denen ich normalerweise beschreibe, wie schlecht die Rennbedingungen sein können, ist die Therac 25- Situation.
"Einfach eine Sperre draufstecken" ist die Mentalität von jemandem, der nicht auf schwierige Bugs mit Multithreading gestoßen ist. Und es ist möglich, dass er denkt , dass Sie den Ernst der Situation überbewerten (ich nicht - es ist möglich, Dinge in die Luft zu jagen oder Menschen mit Race Condition Bugs zu töten, insbesondere mit eingebetteter Software, die in Autos landet).
quelle
Gleichzeitige Anwendungen sind nicht deterministisch. Mit der außergewöhnlich geringen Menge an Gesamtcode, die der Programmierer als anfällig erkannt hat, können Sie nicht steuern, wann ein Teil eines Threads / Prozesses in Bezug auf einen Teil eines anderen Threads ausgeführt wird. Das Testen ist schwieriger, dauert länger und es ist unwahrscheinlich, dass alle Fehler im Zusammenhang mit der Parallelität gefunden werden. Wenn Defekte gefunden werden, die dann subtil sind, können sie nicht konsistent reproduziert werden, weshalb das Reparieren schwierig ist.
Daher ist die einzige richtige gleichzeitige Anwendung eine, die nachweislich richtig ist, was in der Softwareentwicklung nicht oft praktiziert wird. Infolgedessen ist die Antwort von S.Lot der beste allgemeine Rat, da die Weitergabe von Nachrichten relativ einfach als richtig zu beweisen ist.
quelle
Kurze Antwort in zwei Worten: BEOBACHTBARER NONDETERMINISMUS
Lange Antwort: Es hängt davon ab, welchen Ansatz für die gleichzeitige Programmierung Sie bei Ihrem Problem verwenden. In dem Buch Konzepte, Techniken und Modelle der Computerprogrammierung erläutern die Autoren vier praktische Hauptansätze für das Schreiben von parallelen Programmen:
Der einfachste dieser vier Ansätze, abgesehen von der offensichtlichen sequentiellen Programmierung, ist die deklarative Parallelität , da die mit diesem Ansatz geschriebenen Programme keinen beobachtbaren Nichtdeterminismus aufweisen . Mit anderen Worten, es gibt keine Rennbedingungen , da die Rennbedingungen nur ein beobachtbares nicht deterministisches Verhalten sind.
Das Fehlen eines beobachtbaren Nichtdeterminismus bedeutet jedoch, dass es einige Probleme gibt, die wir mit deklarativer Parallelität nicht lösen können. Hier kommen die letzten beiden nicht ganz einfachen Ansätze ins Spiel. Der nicht so einfache Teil ist eine Folge des beobachtbaren Nichtdeterminismus. Jetzt fallen beide unter das Stateful Concurrent-Modell und sind auch in ihrer Ausdruckskraft gleichwertig. Aufgrund der ständig wachsenden Anzahl von Kernen pro CPU scheint sich die Branche jedoch in letzter Zeit mehr für die gleichzeitige Übermittlung von Nachrichten interessiert zu haben, wie dies an der Zunahme von Bibliotheken für die Nachrichtenübermittlung (z. B. Akka für JVM) oder Programmiersprachen (z. B. Erlang ) zu erkennen ist. .
Die zuvor erwähnte Akka-Bibliothek, die auf einem theoretischen Actor-Modell basiert, vereinfacht das Erstellen von gleichzeitigen Anwendungen, da Sie sich nicht mehr mit Sperren, Monitoren oder Transaktionen befassen müssen. Zum anderen erfordert es einen anderen Lösungsansatz, nämlich das Denken, wie Akteure hierarchisch zusammengesetzt werden können. Man könnte sagen, dass es eine völlig andere Denkweise erfordert, die am Ende sogar noch schwieriger sein kann als die Verwendung von gemeinsam genutzter Parallelität im Klartext.
Die gleichzeitige Programmierung ist aufgrund des beobachtbaren Nichtdeterminismus schwierig , aber wenn der richtige Ansatz für das gegebene Problem und die richtige Bibliothek, die diesen Ansatz unterstützt, verwendet wird, können viele Probleme vermieden werden.
quelle
Mir wurde zuerst beigebracht, dass es Probleme aufwerfen könnte, indem man ein einfaches Programm sah, das 2 Threads startete und beide von 1-100 gleichzeitig auf die Konsole druckten. Anstatt von:
Sie erhalten etwas mehr davon:
Führen Sie es erneut aus, und Sie erhalten möglicherweise ganz andere Ergebnisse.
Die meisten von uns sind darauf vorbereitet, dass unser Code nacheinander ausgeführt wird. Bei den meisten Multithreading-Verfahren können wir dies nicht als "out of the box" voraussetzen.
quelle
Versuchen Sie, mehrere Hämmer zu verwenden, um ein Bündel eng beieinander liegender Nägel gleichzeitig zu zerdrücken, ohne dass sich jemand mit den Hämmern verständigt.
Eskaliere dies, um ein Haus zu bauen.
Versuche nachts zu schlafen, wenn du der Architekt bist. :)
quelle
Einfacher Teil: Verwenden Sie Multithreading mit zeitgemäßen Features von Frameworks, Betriebssystemen und Hardware, wie Semaphoren, Warteschlangen, Interlocked Counters, Atomic Boxed Types usw.
Schwieriger Teil: Implementieren Sie die Features selbst, indem Sie zunächst keine Features verwenden. Möglicherweise gibt es nur wenige sehr eingeschränkte Hardwarefunktionen, die sich beispielsweise nur auf die Gewährleistung der Taktkohärenz für mehrere Kerne stützen.
quelle