Parallelität: Wie nähern Sie sich dem Entwurf und debuggen die Implementierung?


37

Ich entwickle seit einigen Jahren parallele Systeme und habe trotz meiner mangelnden formalen Ausbildung (dh ohne Abschluss) ein ziemlich gutes Verständnis für dieses Thema. Es gibt ein paar neue Sprachen, über die in letzter Zeit zumindest gesprochen wurde und die die Parallelität vereinfachen sollen, wie Erlang und Go. Offenbar spiegelt ihr Ansatz zur Parallelität meine eigene Erfahrung darin wider, Systeme skalierbar zu machen und die Vorteile mehrerer Kerne / Prozessoren / Maschinen zu nutzen.

Ich stelle jedoch fest, dass es nur sehr wenige Tools gibt, mit denen Sie veranschaulichen können, was Sie beabsichtigen, und die sicherstellen, dass Sie Ihrer ursprünglichen Vision zumindest nahe kommen. Das Debuggen von gleichzeitigem Code kann ein Albtraum mit Sprachen sein, die nicht für die gleichzeitige Verwendung entwickelt wurden (wie C / C ++, C #, Java usw.). Insbesondere kann es nahezu unmöglich sein, Bedingungen wiederherzustellen, die auf einem System in Ihrer Entwicklungsumgebung ohne Weiteres auftreten.

Was sind Ihre Ansätze zum Entwerfen eines Systems für Parallelität und Parallelverarbeitung? Beispiele:

  • Wie finden Sie heraus, was gleichzeitig erfolgen kann und was aufeinanderfolgend sein muss?
  • Wie können Sie Fehlerbedingungen reproduzieren und anzeigen, was während der Ausführung der Anwendung geschieht?
  • Wie visualisieren Sie die Interaktionen zwischen den verschiedenen gleichzeitigen Teilen der Anwendung?

Für einige habe ich meine eigenen Antworten, aber ich möchte auch ein bisschen mehr lernen.

Bearbeiten

Bisher haben wir viele gute Beiträge. Viele der verlinkten Artikel sind sehr gut und ich habe bereits einige davon gelesen.

Meine persönlichen Erfahrungen mit der gleichzeitigen Programmierung lassen mich glauben, dass Sie eine andere Denkweise benötigen als bei der sequentiellen Programmierung. Die mentale Kluft ist wahrscheinlich so groß wie der Unterschied zwischen objektorientierter Programmierung und prozeduraler Programmierung. Ich möchte, dass diese Fragen sich mehr auf die Denkprozesse konzentrieren, die notwendig sind (dh auf die Theorie), um die Antworten systematisch zu analysieren. Wenn Sie konkretere Antworten geben, ist es hilfreich, ein Beispiel anzugeben - etwas, das Sie persönlich durchlaufen haben.

Ziel für die Bounty

Sag mir nicht, was ich tun soll. Das habe ich schon im Griff. Sag mir was du machst. Sagen Sie mir, wie Sie diese Probleme lösen.


Das ist eine gute Frage - viel mögliche Tiefe. Ich habe auch einige gute Erfahrungen mit Multithread-Anwendungen in Java gemacht, möchte aber mehr erfahren.
Michael K

Bisher haben wir einige gute Antworten. Möchte jemand einen Stich wagen, wenn Sie möchten, dass Sie in diesem Bereich helfen müssen?
Berin Loritsch

Totalview Debugger für die gleichzeitige Codierung ist ein ziemlich nützliches Werkzeug, braucht ein bisschen eine Lernkurve obwohl - totalviewtech.com/products/totalview.html
Fanatic23

Vielleicht hilft Ihnen die Protokollierung bei zwei letzten Fragen.
Amir Rezaei

Was ich suche, sind die Prozesse der Menschen. Dies sind Bereiche, in denen die von mir verwendeten Tools nicht ausreichend sind, aber die Arbeit erledigen können. Ich mache mir weniger Sorgen, wenn ich den Artikel eines anderen zitiere, als vielmehr um die Methodik.
Berin Loritsch

Antworten:


11

Ich entwickle seit einigen Jahren parallele Systeme und habe trotz meiner mangelnden formalen Ausbildung (dh ohne Abschluss) ein ziemlich gutes Verständnis für dieses Thema.

Viele der besten Programmierer, die ich kenne, haben die Universität nicht abgeschlossen. Ich habe Philosophie studiert.

C / C ++, C #, Java usw.). Insbesondere kann es nahezu unmöglich sein, Bedingungen wiederherzustellen, die auf einem System in Ihrer Entwicklungsumgebung ohne Weiteres auftreten.

Ja

Wie finden Sie heraus, was gleichzeitig erfolgen kann und was aufeinanderfolgend sein muss?

Wir beginnen normalerweise mit einer 1000 Meilen hohen Metapher, um unsere Architektur für uns selbst (erstens) und für andere (zweitens) zu verdeutlichen.

Bei diesem Problem haben wir immer eine Möglichkeit gefunden, die Sichtbarkeit von gleichzeitigen Objekten auf nicht gleichzeitige Objekte zu beschränken.

In letzter Zeit habe ich Actors in Scala entdeckt und festgestellt, dass meine alten Lösungen eine Art "Miniactors" sind, viel weniger mächtig als Scala. Mein Vorschlag ist also, von dort aus zu beginnen.

Ein weiterer Vorschlag besteht darin, so viele Probleme wie möglich zu überspringen: Wir verwenden beispielsweise den zentralen Cache (Terrakotta), anstatt Karten im Speicher zu belassen, Rückrufe der inneren Klasse anstelle synchronisierter Methoden zu verwenden, Nachrichten zu senden, anstatt Shared Memory zu schreiben usw.

Mit Scala ist es sowieso viel einfacher.

Wie können Sie Fehlerbedingungen reproduzieren und anzeigen, was während der Ausführung der Anwendung geschieht?

Keine wirkliche Antwort hier. Wir haben einige Unit-Tests auf Parallelität und eine Load-Test-Suite, um die Anwendung so stark wie möglich zu belasten.

Wie visualisieren Sie die Interaktionen zwischen den verschiedenen gleichzeitigen Teilen der Anwendung?

Wieder keine richtige Antwort: Wir entwerfen unsere Metapher auf dem Whiteboard und versuchen sicherzustellen, dass es keine Konflikte auf der architektonischen Seite gibt.

Für Arch meine ich hier die Definition von Neal Ford: SW-Architektur ist alles, was später nur sehr schwer zu ändern sein wird.

Die Programmierung lässt mich glauben, dass Sie eine andere Denkweise benötigen als die sequentielle Programmierung.

Vielleicht, aber für mich ist es einfach unmöglich, parallel zu denken. Entwerfen Sie daher unsere Software besser so, dass kein paralleles Denken erforderlich ist, und verwenden Sie klare Leitplanken, um Abstürze zwischen parallelen Spuren zu vermeiden.


6

Für mich dreht sich alles um die Daten. Brechen Sie Ihre Daten richtig und die parallele Verarbeitung ist einfach. Alle Probleme mit Retention, Deadlocks und so gehen weg.

Ich weiß, dass dies nicht der einzige Weg ist, um zu parallelisieren, aber für mich ist es der mit Abstand nützlichste.

Zur Veranschaulichung eine (nicht ganz so schnelle) Geschichte:

Ich habe von 2007 bis 2009 an einem großen Finanzsystem (Börsenkontrollsystem) gearbeitet, und das Verarbeitungsvolumen der Daten war sehr groß. Zur Veranschaulichung: Alle Berechnungen, die für ein einzelnes Konto eines Kunden durchgeführt wurden, dauerten auf einer durchschnittlichen Arbeitsstation ungefähr 1 bis 3 Sekunden, und es gab mehr als 30.000 Konten. Das Schließen des Systems war jede Nacht ein großes Problem für die Benutzer (in der Regel mehr als 6 Stunden Verarbeitungszeit, ohne dass sie eine Fehlerquote hatten).

Weitere Untersuchungen des Problems ergaben, dass die Berechnungen auf mehreren Computern parallelisiert werden konnten, der alte Datenbankserver (ein SQL 2000-Server, der SQL 6.5 emuliert) jedoch weiterhin einen enormen Engpass aufwies.

Es war ziemlich klar, dass unser minimales Verarbeitungspaket die Berechnung eines einzelnen Kontos war und der größte Engpass die Aufbewahrung des Datenbankservers war (wir konnten an den verschiedenen Verbindungen von "sp_who" erkennen, die darauf warten, die gleiche Verarbeitung durchzuführen). So lief der parallele Prozess ab:

1) Ein einzelner Produzent, der für das sequentielle Lesen oder Schreiben der Datenbank verantwortlich ist. Hier ist keine Parallelität zulässig. Der Produzent hat eine Reihe von Aufträgen für die Verbraucher vorbereitet. Die Datenbank gehörte ausschließlich diesem Hersteller.

2) Mehrere Verbraucher an mehreren Maschinen. Jeder der Verbraucher erhielt ein ganzes Datenpaket aus der Warteschlange, das zur Berechnung bereit war. Jede Deqeue-Operation wird synchronisiert.

3) Nach der Berechnung hat jeder Konsument die Daten an den Produzenten in eine speichersynchrone Warteschlange zurückgesendet, um die Daten zu erhalten.

Es gab mehrere Kontrollpunkte, mehrere Mechanismen, um sicherzustellen, dass die Transaktionen korrekt gespeichert wurden (keine wurde zurückgelassen), aber die ganze Arbeit hat sich gelohnt. Letztendlich haben die Berechnungen auf 10 Computer (plus Producer / Queue-Computer) die Schließzeit des gesamten Systems auf 15 Minuten verkürzt.

Allein die Behebung der Aufbewahrungsprobleme, die durch das schlechte Parallelitätsmanagement von SQL 6.5 verursacht wurden, hatte uns einen großen Vorteil verschafft. Der Rest war ziemlich linear, jeder neue Computer, der zum "Raster" hinzugefügt wurde, verringerte die Verarbeitungszeit, bis wir die "maximale Effizienz" der sequentiellen Lese- / Schreibvorgänge in der Datenbank erreichten.


2

Das Arbeiten in Multithreading-Umgebungen ist schwierig und erfordert die Codierungsdisziplin. Sie müssen die richtigen Richtlinien für das Aufheben der Sperre, das Aufheben der Sperre, den Zugriff auf globale Variablen usw. befolgen.

Lassen Sie mich versuchen, Ihre Frage nacheinander zu beantworten

* How do you figure out what can be made concurrent vs. what has to be sequential?

Verwenden Sie Parallelität für

1) Polling: - Benötigen Sie einen Thread, um fortlaufend etwas abzufragen, oder senden Sie das Update regelmäßig. (Konzepte wie Heart-Bits, die in regelmäßigen Abständen Daten an den zentralen Server senden, um zu bestätigen, dass ich am Leben bin.)

2) Die Operationen, die schwere I / O haben, könnten parallel ausgeführt werden. Das beste Beispiel ist Logger. Der Logger-Thread könnte ein separater Thread sein.

3) Ähnliche Aufgaben für verschiedene Daten. Wenn es eine Aufgabe gibt, die sich auf verschiedene Daten bezieht, aber sehr ähnlich ist, können verschiedene Threads dies tun. Bestes Beispiel sind Serveranforderungen.

Und natürlich auch viele andere, je nach Anwendung.

* How do you reproduce error conditions and view what is happening as the application executes?

Verwenden von Protokollen und Debug-Ausdrucken in den Protokollen. Versuchen Sie, auch die Thread-ID zu protokollieren, damit Sie sehen können, was in den einzelnen Threads geschieht.
Eine Möglichkeit, eine Fehlerbedingung zu erzeugen, besteht darin, die absichtliche Verzögerung (im Debug-Code) an den Stellen abzulegen, an denen das Problem Ihrer Meinung nach auftritt, und diesen Thread gewaltsam anzuhalten. Ähnliche Dinge können auch in Debuggern gemacht werden, aber ich habe es bisher nicht getan.

* How do you visualize the interactions between the different concurrent parts of the application?

Stecken Sie die Protokolle in Ihre Schlösser, damit Sie wissen, wer was und wann sperrt und wer versucht hat, sie zu sperren. Wie ich bereits sagte, versuche, die Thread-ID in das Protokoll aufzunehmen, um zu verstehen, was in jedem Thread vor sich geht.

Dies ist nur mein Ratschlag, der etwa 3 Jahre Arbeit an Multithread-Anwendungen umfasst, und ich hoffe, er hilft.


2
  • Wie finden Sie heraus, was gleichzeitig erfolgen kann und was aufeinanderfolgend sein muss?

Ich würde zunächst die Frage stellen, ob die Anwendung (oder Komponente) tatsächlich von der gleichzeitigen Verarbeitung profitiert, oder, was Laien betrifft - wo liegt der Engpass? Nebenläufigkeit ist natürlich nicht immer ein Gewinn für die Investition, die erforderlich ist, um sie zum Laufen zu bringen. Wenn es wie ein Kandidat aussieht, würde ich von unten nach oben arbeiten - versuchen, die größte Operation oder Gruppe von Operationen zu finden, die ihre Arbeit effektiv für sich alleine erledigen können Operationen - Ich suche Schauspieler .

Durch die Zusammenarbeit mit Erlang habe ich das Konzept der asynchronen Nachrichtenübermittlung und das Actor-Modell für die gleichzeitige Verwendung absolut geliebt - es ist intuitiv, effektiv und übersichtlich.

Off of Understanding Actor Concurrency

Das Akteurmodell besteht aus einigen Schlüsselprinzipien:

  • Kein gemeinsamer Zustand
  • Leichte Prozesse
  • Asynchrone Nachrichtenübermittlung
  • Postfächer zum Puffern eingehender Nachrichten
  • Postfachverarbeitung mit Mustererkennung

Ein Akteur ist ein Prozess, der eine Funktion ausführt. Hier ist ein Prozess ein leichtgewichtiger User-Space-Thread (nicht zu verwechseln mit einem typischen schwergewichtigen Betriebssystemprozess). Akteure teilen niemals den Status und müssen daher niemals um Sperren für den Zugriff auf gemeinsam genutzte Daten konkurrieren. Stattdessen tauschen Akteure Daten aus, indem sie unveränderliche Nachrichten senden. Unveränderliche Daten können nicht geändert werden, sodass Lesevorgänge keine Sperre erfordern.

Das Erlang-Parallelitätsmodell ist einfacher zu verstehen und zu debuggen als das Sperren und Freigeben von Daten. Die Art und Weise, in der Ihre Logik isoliert ist, ist ein einfaches Testen von Komponenten durch Übergeben von Nachrichten.

Bei gleichzeitigen Systemen funktionierte mein Design sowieso in jeder Sprache so - eine Warteschlange, aus der mehrere Threads Daten abrufen, einen einfachen Vorgang ausführen und die Warteschlange wiederholen oder zurückschieben. Erlang erzwingt lediglich unveränderliche Datenstrukturen, um Nebenwirkungen zu vermeiden und die Kosten und die Komplexität beim Erstellen neuer Threads zu verringern.

Dieses Modell ist nicht Erlang-exklusiv, selbst in der Java- und .NET-Welt gibt es Möglichkeiten, dies zu erreichen. Ich würde die Concurrency and Coordination Runtime (CCR) und Relang (es gibt auch Jetlang für Java) betrachten.

  • Wie können Sie Fehlerbedingungen reproduzieren und anzeigen, was während der Ausführung der Anwendung geschieht?

Nach meiner Erfahrung kann ich mich nur dazu verpflichten, alles zu verfolgen / aufzuzeichnen. Jeder Prozess / Thread muss eine Kennung haben und jede neue Arbeitseinheit muss eine Korrelations-ID haben. Sie müssen in der Lage sein, Ihre Protokolle zu durchsuchen und genau zu verfolgen, was wann verarbeitet wurde - ich habe keine Magie gesehen, um dies zu beseitigen.

  • Wie visualisieren Sie die Interaktionen zwischen den verschiedenen gleichzeitigen Teilen der Anwendung?

Siehe oben, es ist hässlich, aber es funktioniert. Das einzige andere, was ich mache, ist die Verwendung von UML-Sequenzdiagrammen - natürlich während der Entwurfszeit -, aber Sie können damit überprüfen, ob Ihre Komponenten so sprechen, wie Sie sie auch wollen.


1

- Meine Antworten sind MS / Visual Studio-spezifisch -

Wie finden Sie heraus, was gleichzeitig erfolgen kann und was aufeinanderfolgend sein muss?

Das wird Domain-Wissen erfordern, es wird keine pauschale Aussage geben, die dies abdeckt.

Wie können Sie Fehlerbedingungen reproduzieren und anzeigen, was während der Ausführung der Anwendung geschieht?

Viel Protokollierung, in der Lage zu sein, die Protokollierung in Produktionsanwendungen ein- / auszuschalten, um sie in der Produktion zu erfassen. VS2010 Intellitrace soll dabei helfen, ich habe es aber noch nicht benutzt.

Wie visualisieren Sie die Interaktionen zwischen den verschiedenen gleichzeitigen Teilen der Anwendung?

Ich habe keine gute Antwort darauf, würde gerne eine sehen.


Die Protokollierung ändert die Ausführung des Codes und kann daher zu dem Fehler führen, nach dem Sie nicht angezeigt werden.
Matthew Read

1

Ich bin nicht einverstanden mit Ihrer Aussage, dass C nicht für die gleichzeitige Verwendung ausgelegt ist. C wurde für die allgemeine Systemprogrammierung entwickelt und kann auf kritische Entscheidungen hinweisen. Dies wird auch in den kommenden Jahren so bleiben. Dies gilt auch dann, wenn die beste Entscheidung möglicherweise darin besteht, C nicht zu verwenden. Außerdem ist die Parallelität in C nur so schwierig, wie Ihr Entwurf komplex ist.

Ich versuche, Sperren nach besten Kräften zu implementieren, mit dem Gedanken, dass letztendlich eine wirklich praktische sperrenfreie Programmierung für mich Realität werden könnte. Mit Sperren meine ich keinen gegenseitigen Ausschluss, sondern nur einen Prozess, der eine sichere Parallelität ohne Schiedsverfahren implementiert. Unter praktisch verstehe ich etwas, das einfacher zu portieren ist als zu implementieren. Ich habe auch sehr wenig formales CS Training, aber ich nehme an, dass ich es mir wünschen darf :)

Im Anschluss daran werden die meisten Fehler, denen ich begegne, relativ flach oder stören mich so sehr, dass ich mich in eine Kneipe zurückziehe. Die Kneipe wird erst dann zu einer attraktiven Option, wenn die Profilerstellung eines Programms die Suche nach zusätzlichen Rennen verlangsamt, die nichts mit dem zu tun haben, wonach ich suche.

Wie bereits erwähnt, handelt es sich bei dem von Ihnen beschriebenen Problem um ein äußerst domänenspezifisches Problem. Ich versuche nur, mit meinem besten Vermögen, jeden Fall zu vermeiden, der ein Schiedsverfahren erfordert (außerhalb meines Prozesses), wann immer dies möglich ist. Wenn das so aussieht, als wäre es ein königlicher Schmerz, überarbeite ich die Option, mehreren Threads oder Prozessen gleichzeitig und unserialisiert Zugriff auf etwas zu gewähren.

Dann werfen Sie 'verteilt' hinein und Schiedsgerichtsbarkeit wird ein Muss. Haben Sie ein konkretes Beispiel?


Um meine Aussage zu verdeutlichen, wurde C nicht speziell für die gleichzeitige Verwendung entwickelt. Dies steht im Gegensatz zu Sprachen wie Go, Erlang und Scala, die explizit unter Berücksichtigung der Parallelität entwickelt wurden. Ich wollte nicht sagen, dass Sie nicht gleichzeitig mit C.
Berin Loritsch

1

Wie können Sie Fehlerbedingungen reproduzieren und anzeigen, was während der Ausführung der Anwendung geschieht?

Wie visualisieren Sie die Interaktionen zwischen den verschiedenen gleichzeitigen Teilen der Anwendung?

Nach meiner Erfahrung lautet die Antwort auf diese beiden Aspekte wie folgt:

Verteilte Ablaufverfolgung

Bei der verteilten Ablaufverfolgung handelt es sich um eine Technologie, mit der Zeitdaten für jede einzelne gleichzeitige Komponente Ihres Systems erfasst und grafisch dargestellt werden. Darstellungen von gleichzeitigen Ausführungen sind immer verschachtelt, sodass Sie sehen können, was parallel ausgeführt wird und was nicht.

Das verteilte Tracing hat seinen Ursprung (natürlich) in verteilten Systemen, die per Definition asynchron sind und eine hohe Gleichzeitigkeit aufweisen. Ein verteiltes System mit verteiltem Tracing ermöglicht Folgendes:

a) wichtige Engpässe identifizieren, b) eine visuelle Darstellung der idealen "Läufe" Ihrer Anwendung erhalten und c) Einblick in das ausgeführte gleichzeitige Verhalten gewähren, d) Zeitdaten erhalten, die zur Beurteilung von Unterschieden zwischen Ihren Änderungen verwendet werden können System (extrem wichtig, wenn Sie starke SLAs haben).

Die Folgen der verteilten Ablaufverfolgung sind jedoch:

  1. Es erhöht den Aufwand für alle gleichzeitig ablaufenden Prozesse, da mehr Code ausgeführt und möglicherweise über ein Netzwerk gesendet werden kann. In einigen Fällen ist dieser Aufwand sehr hoch. Selbst Google verwendet das Tracing-System Dapper nur für eine kleine Teilmenge aller Anforderungen, um die Benutzerfreundlichkeit nicht zu beeinträchtigen.

  2. Es gibt viele verschiedene Tools, die nicht alle miteinander kompatibel sind. Dies wird durch Standards wie OpenTracing etwas verbessert, aber nicht vollständig gelöst.

  3. Hier erfahren Sie nichts über freigegebene Ressourcen und deren aktuellen Status. Möglicherweise können Sie anhand des Anwendungscodes und des angezeigten Diagramms eine Vermutung anstellen, dies ist jedoch in dieser Hinsicht kein nützliches Werkzeug.

  4. Aktuelle Tools setzen voraus, dass Sie über genügend Arbeitsspeicher und Speicherplatz verfügen. Das Hosten eines Zeitreihen-Servers ist möglicherweise nicht billig, abhängig von Ihren Einschränkungen.

Fehlerverfolgungssoftware

Ich verweise oben auf Sentry, vor allem, weil es das am häufigsten verwendete Tool ist und aus gutem Grund - eine Fehlerverfolgungssoftware wie Sentry, die die Laufzeitausführung übernimmt, um eine Stapelverfolgung der aufgetretenen Fehler gleichzeitig an einen zentralen Server weiterzuleiten.

Der Nettovorteil einer solchen dedizierten Software in gleichzeitigem Code:

  1. Doppelte Fehler werden nicht dupliziert . Mit anderen Worten, wenn auf einem oder mehreren gleichzeitigen Systemen dieselbe Ausnahme auftritt, erhöht Sentry einen Vorfallbericht , sendet jedoch nicht zwei Kopien des Vorfalls.

Dies bedeutet, dass Sie herausfinden können, bei welchem ​​gleichzeitigen System welche Art von Fehler auftritt, ohne unzählige gleichzeitige Fehlerberichte durchlaufen zu müssen. Wenn Sie jemals E-Mail-Spam von einem verteilten System erhalten haben, wissen Sie, wie sich die Hölle anfühlt.

Sie können sogar verschiedene Aspekte Ihres gleichzeitigen Systems mit Tags versehen (dies setzt jedoch voraus, dass Sie nicht über genau einen Thread verschachtelt arbeiten, was technisch ohnehin nicht gleichzeitig ist, da der Thread einfach effizient zwischen Tasks wechselt, aber dennoch Ereignishandler verarbeiten muss bis zum Abschluss) und sehen Sie eine Aufschlüsselung der Fehler nach Tag.

  1. Sie können diese Fehlerbehandlungssoftware ändern, um zusätzliche Details zu Ihren Laufzeitausnahmen bereitzustellen. Welche offenen Ressourcen hatte der Prozess? Gibt es eine gemeinsam genutzte Ressource, die dieser Prozess gehalten hat? Welcher Benutzer hat dieses Problem festgestellt?

Zusätzlich zu sorgfältigen Stack-Traces (und Quellzuordnungen, wenn Sie eine verkleinerte Version Ihrer Dateien bereitstellen müssen) können Sie auf einfache Weise feststellen, was die meiste Zeit falsch läuft.

  1. (Sentry-spezifisch) Sie können ein separates Sentry-Berichts-Dashboard für Testläufe des Systems einrichten, mit dem Sie Fehler beim Testen feststellen können.

Zu den Nachteilen einer solchen Software gehören:

  1. Wie alles fügen sie Masse hinzu. Möglicherweise möchten Sie ein solches System nicht für eingebettete Hardware verwenden. Ich empfehle dringend, einen Testlauf einer solchen Software durchzuführen und eine einfache Ausführung mit und ohne diese zu vergleichen, die über ein paar hundert Läufe auf einem inaktiven Computer ausgeführt wurde.

  2. Nicht alle Sprachen werden gleichermaßen unterstützt, da viele dieser Systeme implizit auf das Abfangen einer Ausnahme angewiesen sind und nicht alle Sprachen robuste Ausnahmen aufweisen. Davon abgesehen gibt es Kunden für eine Vielzahl von Systemen.

  3. Sie stellen möglicherweise ein Sicherheitsrisiko dar, da viele dieser Systeme im Wesentlichen als Closed-Source-Systeme ausgeführt werden. In solchen Fällen sollten Sie Ihre Recherchen sorgfältig durchführen oder, falls gewünscht, Ihre eigenen würfeln.

  4. Sie geben Ihnen möglicherweise nicht immer die Informationen, die Sie benötigen. Dies ist ein Risiko bei allen Versuchen, die Sichtbarkeit zu erhöhen.

  5. Die meisten dieser Dienste wurden für hochkonkurrierende Webanwendungen entwickelt, sodass möglicherweise nicht jedes Tool für Ihren Anwendungsfall perfekt ist.

In der Summe : Sichtbarkeit ist der wichtigste Teil eines jeden gleichzeitigen Systems. Die beiden Methoden, die ich oben in Verbindung mit speziellen Dashboards zu Hardware und Daten beschreibe, um ein umfassendes Bild des Systems zu einem bestimmten Zeitpunkt zu erhalten, sind in der Branche weit verbreitet, um genau diesen Aspekt zu berücksichtigen.

Einige zusätzliche Vorschläge

Ich habe mehr Zeit damit verbracht, Code von Leuten zu reparieren, die versucht haben, gleichzeitig auftretende Probleme auf schreckliche Weise zu lösen. Jedes Mal habe ich Fälle gefunden, in denen die folgenden Dinge die Entwicklererfahrung erheblich verbessern können (was genauso wichtig ist wie die Benutzererfahrung):

  • Verlassen Sie sich auf Typen . Die Eingabe dient zur Validierung Ihres Codes und kann zur Laufzeit als zusätzlicher Schutz verwendet werden. Wenn keine Eingabe vorhanden ist, verlassen Sie sich auf Behauptungen und einen geeigneten Fehlerbehandler, um Fehler abzufangen. Gleichzeitiger Code erfordert defensiven Code, und Typen dienen als beste verfügbare Validierungsmethode.

    • Testen Sie Verknüpfungen zwischen Codekomponenten , nicht nur der Komponente selbst. Verwechseln Sie dies nicht mit einem vollständigen Integrationstest, der jede Verbindung zwischen jeder Komponente testet und selbst dann nur nach einer globalen Validierung des Endzustands sucht. Dies ist ein schrecklicher Weg, um Fehler abzufangen.

Ein guter Verbindungstest überprüft, ob die empfangene und die gesendete Nachricht erwartungsgemäß übereinstimmen , wenn eine Komponente isoliert mit einer anderen Komponente kommuniziert. Wenn zwei oder mehr Komponenten für die Kommunikation auf einen gemeinsam genutzten Dienst angewiesen sind, drehen Sie sie alle auf, lassen Sie sie über den zentralen Dienst Nachrichten austauschen und prüfen Sie, ob sie am Ende alle das bekommen, was Sie erwarten.

Wenn Sie Tests, an denen viele Komponenten beteiligt sind, in einen Test der Komponenten selbst und in einen Test der Kommunikation der einzelnen Komponenten aufteilen, gewinnen Sie mehr Vertrauen in die Gültigkeit Ihres Codes. Mit einer solch strengen Anzahl von Tests können Sie Verträge zwischen Diensten erzwingen und unerwartete Fehler abfangen, die auftreten, wenn sie gleichzeitig ausgeführt werden.

  • Verwenden Sie die richtigen Algorithmen, um Ihren Anwendungsstatus zu überprüfen. Ich spreche von einfachen Dingen, z. B. wenn ein Master-Prozess darauf wartet, dass alle seine Mitarbeiter eine Aufgabe erledigen, und nur dann zum nächsten Schritt übergehen möchte, wenn alle Mitarbeiter vollständig erledigt sind - dies ist ein Beispiel für die globale Erkennung Terminierung, für die bekannte Methoden wie Safras Algorithmus existieren.

Einige dieser Tools werden mit Sprachen gebündelt geliefert. Rust beispielsweise garantiert, dass Ihr Code zur Kompilierungszeit keine Race-Bedingungen aufweist, während Go über einen integrierten Deadlock-Detektor verfügt, der auch zur Kompilierungszeit ausgeführt wird. Wenn Sie Probleme erkennen können, bevor sie die Produktion erreichen, ist dies immer ein Gewinn.

Eine allgemeine Faustregel: Entwurf für Fehler in gleichzeitigen Systemen . Stellen Sie sich vor, dass allgemeine Dienste abstürzen oder ausfallen. Dies gilt auch für Code, der nicht über mehrere Computer verteilt ist - gleichzeitiger Code auf einem einzelnen Computer kann auf externen Abhängigkeiten beruhen (z. B. einer freigegebenen Protokolldatei, einem Redis-Server, einem verdammten MySQL-Server), die jederzeit verschwinden oder entfernt werden können .

Der beste Weg, dies zu tun, besteht darin, den Anwendungsstatus von Zeit zu Zeit zu validieren. Lassen Sie für jeden Dienst Integritätsprüfungen durchführen und stellen Sie sicher, dass die Verbraucher dieses Dienstes über den schlechten Zustand informiert werden. Moderne Container-Tools wie Docker machen das ganz gut und sollten dazu genutzt werden, Dinge zu sandboxen.

Wie finden Sie heraus, was gleichzeitig und was sequentiell gemacht werden kann?

Eine der größten Lektionen, die ich bei der Arbeit mit einem System gelernt habe, das gleichzeitig sehr häufig ausgeführt wird, ist folgende: Es kann nie genug Metriken geben . Metriken sollten absolut alles in Ihrer Anwendung steuern - Sie sind kein Ingenieur, wenn Sie nicht alles messen.

Ohne Metriken können Sie nicht ein paar sehr wichtige Dinge tun:

  1. Beurteilen Sie den Unterschied, den Änderungen am System bewirken. Wenn Sie nicht wissen, ob der Abstimmknopf A die Metrik B erhöht und die Metrik C verringert hat, wissen Sie nicht, wie Sie Ihr System reparieren können, wenn Personen unerwartet bösartigen Code auf Ihr System übertragen (und diesen an Ihr System senden). .

  2. Verstehe, was du als nächstes tun musst, um Dinge zu verbessern. Solange Sie nicht wissen, dass in Anwendungen nur noch wenig Arbeitsspeicher zur Verfügung steht, können Sie nicht erkennen, ob Sie mehr Arbeitsspeicher benötigen oder mehr Festplatten für Ihre Server kaufen sollten.

Metriken sind so wichtig und essentiell, dass ich mich bewusst bemüht habe, zu planen, was ich messen möchte, bevor ich überhaupt darüber nachdenke, was ein System benötigt. Tatsächlich sind Metriken so wichtig, dass ich glaube, dass sie die richtige Antwort auf diese Frage sind: Sie wissen nur, was sequentiell oder gleichzeitig gemacht werden kann, wenn Sie messen, was die Bits in Ihrem Programm tun. Das richtige Design verwendet Zahlen, keine Vermutungen.

Davon abgesehen gibt es sicherlich ein paar Faustregeln:

  1. Sequentielle impliziert Abhängigkeit. Zwei Prozesse sollten sequentiell ablaufen, wenn einer in irgendeiner Weise von dem anderen abhängig ist. Prozesse ohne Abhängigkeiten sollten gleichzeitig ablaufen. Planen Sie jedoch eine Möglichkeit, Fehler im Upstream zu behandeln, die nicht verhindert, dass Prozesse im Downstream auf unbestimmte Zeit warten.

  2. Mischen Sie niemals eine E / A-gebundene Aufgabe mit einer CPU-gebundenen Aufgabe auf demselben Kern. Schreiben Sie (zum Beispiel) keinen Webcrawler, der zehn gleichzeitige Anforderungen im selben Thread startet, sie kratzt, sobald sie eingehen, und erwarten Sie eine Skalierung auf fünfhundert - E / A-Anforderungen werden parallel in eine Warteschlange gestellt, aber Die CPU durchläuft sie weiterhin seriell. (Dieses ereignisgesteuerte Single-Thread-Modell ist sehr beliebt, aber es ist aufgrund dieses Aspekts begrenzt. Statt dies zu verstehen, wringen die Leute einfach ihre Hände und sagen, dass Node nicht skaliert, um Ihnen ein Beispiel zu geben.)

Ein einzelner Thread kann viel E / A-Arbeit leisten. Verwenden Sie jedoch Threadpools, die zusammen alle Kerne belegen, um die Parallelität Ihrer Hardware voll auszunutzen. Im obigen Beispiel wird das Starten von fünf Python-Prozessen (von denen jeder einen Kern auf einem Sechs-Kern-Rechner verwenden kann) nur für die CPU-Arbeit und eines sechsten Python-Threads nur für die E / A-Arbeit viel schneller skaliert, als Sie denken.

Die CPU-Parallelität kann nur über einen dedizierten Threadpool genutzt werden. Ein einzelner Thread ist oft genug für viele E / A-gebundene Arbeiten. Aus diesem Grund lassen sich ereignisgesteuerte Webserver wie Nginx besser skalieren (sie leisten nur E / A-gebundene Arbeit) als Apache (bei dem E / A-gebundene Arbeit mit etwas verbunden wird, das CPU erfordert und einen Prozess pro Anforderung startet) Zehntausende von parallel empfangenen GPU-Berechnungen sind eine schreckliche Idee.


0

Nun, für den Überprüfungsprozess, wenn ich ein großes gleichzeitiges System entwerfe, neige ich dazu, das Modell mit LTSA - Labeled Transition System Analyzer zu testen . Es wurde von meinem alten Tutor entwickelt, der so etwas wie ein Veteran auf dem Gebiet der Nebenläufigkeit ist und jetzt Head of Computing bei Imperial ist.

Was das Gleichzeitige angeht, gibt es statische Analysegeräte, die das zeigen, glaube ich, obwohl ich dazu neige, nur Zeitpläne für kritische Abschnitte zu zeichnen, so wie Sie es für das Projektmanagement tun würden. Identifizieren Sie dann Abschnitte, die denselben Vorgang wiederholt ausführen. Ein schneller Weg besteht darin, nur Schleifen zu finden, da dies in der Regel die Bereiche sind, die von der Parallelverarbeitung profitieren.


0

Wie finden Sie heraus, was gleichzeitig erfolgen kann und was aufeinanderfolgend sein muss?

So ziemlich alles, was Sie schreiben, kann die Nebenläufigkeit ausnutzen, insbesondere den Anwendungsfall "Teilen und Erobern". Eine viel bessere Frage ist, was gleichzeitig sein soll.

Joseph Albaharis Threading in C # listet fünf gebräuchliche Verwendungen auf.

Multithreading hat viele Verwendungsmöglichkeiten. hier sind die häufigsten:

Pflege einer ansprechenden Benutzeroberfläche

Indem zeitaufwändige Aufgaben auf einem parallelen "Worker" -Thread ausgeführt werden, kann der Haupt-UI-Thread weiterhin Tastatur- und Mausereignisse verarbeiten.

Effiziente Nutzung einer ansonsten blockierten CPU

Multithreading ist nützlich, wenn ein Thread auf eine Antwort von einem anderen Computer oder einer anderen Hardware wartet. Während ein Thread während der Ausführung der Aufgabe blockiert ist, können andere Threads den ansonsten nicht belasteten Computer nutzen.

Parallele Programmierung

Code, der intensive Berechnungen ausführt, kann auf Multicore- oder Multiprozessor-Computern schneller ausgeführt werden, wenn die Arbeitslast in einer Divide-and-Conquer-Strategie auf mehrere Threads aufgeteilt wird (siehe Teil 5).

Spekulative Ausführung

Auf Multicore-Computern können Sie die Leistung manchmal verbessern, indem Sie vorhersagen, was möglicherweise getan werden muss, und dies dann im Voraus tun. LINQPad verwendet diese Technik, um die Erstellung neuer Abfragen zu beschleunigen. Eine Variante besteht darin, eine Reihe verschiedener Algorithmen parallel auszuführen, die alle dieselbe Aufgabe lösen. Wer zuerst fertig ist, gewinnt - dies ist effektiv, wenn Sie nicht im Voraus wissen können, welcher Algorithmus am schnellsten ausgeführt wird.

Zulassen, dass Anforderungen gleichzeitig verarbeitet werden

Clientanforderungen können auf einem Server gleichzeitig eingehen und müssen daher parallel verarbeitet werden (.NET Framework erstellt automatisch Threads für diese Anforderungen, wenn Sie ASP.NET, WCF, Webdienste oder Remoting verwenden). Dies kann auch auf einem Client nützlich sein (z. B. bei der Abwicklung von Peer-to-Peer-Netzwerken - oder sogar bei mehreren Anforderungen des Benutzers).

Wenn Sie nicht versuchen, eines der oben genannten Dinge zu tun, sollten Sie sich wahrscheinlich Gedanken darüber machen.

Wie können Sie Fehlerbedingungen reproduzieren und anzeigen, was während der Ausführung der Anwendung geschieht?

Wenn Sie .NET verwenden und Anwendungsfälle geschrieben haben, können Sie CHESS verwenden, um bestimmte Thread-Interleaving-Bedingungen wiederherzustellen, mit denen Sie Ihr Update testen können.

Wie visualisieren Sie die Interaktionen zwischen den verschiedenen gleichzeitigen Teilen der Anwendung?

Es kommt auf den Kontext an. Für Arbeiterszenarien denke ich an einen Manager-Untergebenen. Manager fordert den Untergebenen auf, etwas zu tun, und wartet auf Statusaktualisierungen.

Bei gleichzeitigen Aufgaben, die nichts miteinander zu tun haben, denke ich an Aufzüge oder Autos in getrennten Fahrspuren.

Zur Synchronisation denke ich manchmal an Ampeln oder Abbiegestile.

Wenn Sie C # 4.0 verwenden, sollten Sie sich auch die Task Parallel Library ansehen


0

Meine Antwort auf diese Fragen lautet:

  • Wie finden Sie heraus, was gleichzeitig erfolgen kann und was aufeinanderfolgend sein muss?

Zuerst muss ich wissen, warum ich Parallelität verwenden soll, weil ich herausgefunden habe, dass die Leute von der Idee hinter der Parallelität begeistert sind, aber nicht immer über das Problem nachdenken, das sie zu lösen versuchen.

Wenn Sie eine reale Situation wie Warteschlangen, Arbeitsabläufe usw. simulieren müssen, müssen Sie höchstwahrscheinlich einen gleichzeitigen Ansatz verwenden.

Jetzt, da ich weiß, dass ich es verwenden sollte, ist es an der Zeit, den Kompromiss zu analysieren. Wenn Sie viele Prozesse haben, denken Sie möglicherweise über den Kommunikationsaufwand nach so.)

  • Wie können Sie Fehlerbedingungen reproduzieren und anzeigen, was während der Ausführung der Anwendung geschieht?

Ich bin kein Experte in dieser Angelegenheit, aber ich denke, dass dies für gleichzeitige Systeme nicht der richtige Ansatz ist. Es sollte ein theoretischer Ansatz gewählt werden, bei dem die 4 Deadlock-Anforderungen in kritischen Bereichen berücksichtigt werden:

  1. Non preemptiveness
  2. Warte und warte
  3. Ausschluss
  4. Kreisförmige Kette

    • Wie visualisieren Sie die Interaktionen zwischen den verschiedenen gleichzeitigen Teilen der Anwendung?

Ich versuche zuerst zu identifizieren, wer die Teilnehmer an den Interaktionen sind, dann wie sie kommunizieren und mit wem. Schließlich helfen mir Grafiken und Interaktionsdiagramme beim Visualisieren. Mein gutes altes Whiteboard kann von keinem anderen Medium geschlagen werden.


0

Ich werde stumpf sein. Ich liebe Werkzeuge. Ich benutze viele Werkzeuge. Mein erster Schritt besteht darin, die beabsichtigten Wege für den Staatsfluss festzulegen. Mein nächster Schritt ist, herauszufinden, ob es sich lohnt oder ob der erforderliche Informationsfluss den Code zu oft seriell macht. Dann werde ich versuchen, einige einfache Modelle zu entwerfen. Diese können von einem Stapel roher Zahnstocherskulpturen bis zu einigen einfachen ähnlichen Beispielen in Python reichen. Als nächstes schaue ich mir ein paar meiner Lieblingsbücher an, wie das kleine Buch mit den Semaphoren, und finde heraus, ob jemand bereits eine bessere Lösung für mein Problem gefunden hat.

Dann fange ich an zu codieren.
Ich mache nur Spaß. Ein bisschen mehr Forschung zuerst. Ich setze mich gerne mit einem anderen Hacker zusammen und gehe eine erwartete Ausführung des Programms auf hohem Niveau durch. Wenn Fragen auftauchen, gehen wir auf eine niedrigere Ebene. Es ist wichtig herauszufinden, ob jemand anderes Ihre Lösung gut genug versteht, um sie warten zu können.

Zum Schluss beginne ich mit dem Codieren. Ich versuche es zuerst sehr einfach zu halten. Nur der Codepfad, nichts Besonderes. Bewege so wenig Staat wie möglich. Vermeiden Sie Schreibvorgänge. Vermeiden Sie Lesevorgänge, die zu Konflikten mit Schreibvorgängen führen können. Vermeiden Sie vor allem Schreibvorgänge, die zu Konflikten mit Schreibvorgängen führen können. Es ist sehr leicht festzustellen, dass Sie eine positiv toxische Zahl von diesen haben und dass Ihre schöne Lösung plötzlich nicht mehr als ein Cache-Thrashing-Ansatz ist.

Eine gute Regel ist, Frameworks zu verwenden, wo immer Sie können. Wenn Sie selbst grundlegende Threading-Komponenten schreiben, wie z. B. gut synchronisierte Datenstrukturen oder verbotene, tatsächliche Synchro-Primitive, werden Sie mit ziemlicher Sicherheit das ganze Bein umhauen.

Schließlich Werkzeuge. Das Debuggen ist sehr schwierig. Ich verwende valgrind \ callgrind unter Linux in Verbindung mit PIN und parallele Studios unter Windows. Versuchen Sie nicht, dieses Zeug von Hand zu debuggen. Sie können wahrscheinlich. Aber du wirst wahrscheinlich wünschen, du hättest es nicht. Zehn Stunden, in denen Sie einige leistungsstarke Tools und einige gute Modelle beherrschen, ersparen Ihnen Hunderte Stunden später.

Arbeiten Sie vor allem inkrementell. Sorgfältig arbeiten. Schreiben Sie nicht gleichzeitig Code, wenn Sie müde sind. Schreiben Sie es nicht, während Sie hungrig sind. In der Tat, wenn Sie es vermeiden können, schreiben Sie es einfach nicht. Parallelität ist schwierig, und ich habe festgestellt, dass viele Apps, in denen sie als Funktion aufgeführt ist, sie häufig als einzige Funktion enthalten.

Fazit:
Beginnen:
Denken
Sprechen
Test
Schreiben einfach
Lesen
Test
Schreiben
Debuggen
GOTO Beginnen

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.