Konkrete Beispiele
Ich möchte einige Beispiele aus der Praxis hinzufügen und sie mit der Welt der Softwareentwicklung verbinden. Betrachten Sie zunächst etwas, von dem ich hoffe, dass es Ihrer intuitiven Definition von "synchron" entspricht: das Aufblitzen von Glühwürmchen unter bestimmten Umständen. Zweitens betrachten Sie die 4x100 Olympischen Relais Frauenrennen . Drittens, denken Sie an den alten Trumpf aus Militärfilmen: "Männer, synchronisieren Sie Ihre Uhren!"
Überlegen wir uns jetzt, was los ist. Beginnen wir mit der Beobachtung, dass all diese Dinge Prozesse oder zeitlich verlängerte Entitäten sind . Es macht keinen Sinn zu sagen, dass eine Schüssel "synchron" und Rock "asynchron" ist. Zweitens braucht man zwei für den Tango . Man kann nicht sagen, dass "ein Läufer synchron ist". Mit was synchronisieren? Damit zwei Prozesse gleichzeitig etwas tun können, muss einer oder beide warten , es sei denn, sie haben bereits genau dieselbe Frequenz und Phase .
Analyse
Wenn die Wörterbuchdefinition besagt, dass zwei Entitäten gleichzeitig auftreten oder existieren, passt dies sehr gut zum Konzept des Lichts von Glühwürmchen. Unglücklicherweise kann man schlampig sagen, dass das Licht "synchron" ist, wenn man sagt, dass die Beleuchtungsprozesse der Glühwürmchen synchronisiert sind.
Wie kann es also einer Gruppe von Glühwürmchen, die vermutlich nicht über Apple SmartWatch und NTP verfügen, gelingen, ihre hinteren Enden gleichzeitig zu blitzen? Nun, es ist ziemlich einfach, wenn sie die Möglichkeit haben, ein einheitliches Tempo einzustellen und kleine Anpassungen vorzunehmen. Sie blinken nur, und wenn mehr Leute direkt nach ihnen blinken, verlangsamen sie sich (erhöhen Sie die Verzögerung), während sie sich beschleunigen (verringern Sie die Verzögerung), wenn sie direkt vor ihnen blinken. So können sie mit einem einfachen Feedback-Prozess im Wesentlichen dasselbe Tempo und dieselbe Phase erreichen. Die wichtige Beobachtung hierbei ist, dass sie Synchronität erreichen, indem sie auf den richtigen Moment warten, um zu blinken .
Das 4x100-Rennen ist interessant, weil Sie beide Formen des Prozess-Timings in Aktion sehen: Die Läufer in einem Team sind synchronisiert, während die Läufer in verschiedenen Teams "asynchron" sind. Der zweite Läufer im Relais muss warten, bis der erste Läufer die Transferzone betritt . Die Übergabe ist ein synchrones Ereignis zwischen diesen beiden Läufern. Den Läufern auf verschiedenen Fahrspuren ist es jedoch egal, was auf einer anderen Fahrspur passiert , und sie verlangsamen mit Sicherheit nicht und geben ihre Handoffs synchron ab. Jede Spur von Läufern ist in Bezug aufeinander asynchron. Wiederum sehen wir, dass die Synchronisation das Warten erfordert, während die Asynchronität dies nicht tut.
Schließlich werden die Soldaten in einem Unternehmen (Zug, Feuerteam usw.) müssen ihre Uhren synchronisieren , so dass sie den Feind angreifen kann zugleich . Es kann sein, dass einige Soldaten ihre Positionen vor anderen erreichen oder die Gelegenheit haben, früher auf den Feind zu schießen. Ein gleichzeitiger Angriff ist jedoch aufgrund des Überraschungsmoments im Allgemeinen effektiver als ein willkürlicher Angriff. Um eine Synchronität zu erreichen, müssen viele Soldaten auf die festgelegte Zeit warten, um zu handeln.
Merkmal definieren
Warum diese Betonung auf das Warten? Das liegt daran, dass das Warten das bestimmende Merkmal ist, das synchrone von asynchronen Prozessen unterscheidet. Wenn Sie zwei Prozesse haben, von denen Sie nichts wissen, sollten Sie standardmäßig davon ausgehen, dass sie asynchron sind. Beispielsweise sind eine Paketzustellung und ein vorbeifahrender Krankenwagen höchstwahrscheinlich nicht synchronisiert. Um zu demonstrieren, dass zwei Prozesse tatsächlich synchronisiert sind, müssen Sie einen ganz besonderen Zeitpunkt finden: den Synchronisationspunkt .
Ein Zusteller, der ein Paket abgibt, und ein Krankenwagen, der jemanden ins Krankenhaus bringt, teilen im Allgemeinen keine Zeitpunkte, die wir als "Synchronisationspunkt" identifizieren. Auf der anderen Seite haben Glühwürmchen, die gleichzeitig blinken, jedes Mal einen Synchronisationspunkt, wenn sie blinken, Staffelläufer haben jedes Mal einen Synchronisationspunkt, wenn sie den Staffelstab abgeben, und Soldaten haben einen Synchronisationspunkt, wenn sie ihren Angriff starten. Wenn Sie einen oder mehrere Synchronisationspunkte identifizieren können, werden die Prozesse synchronisiert . Dies sollte leicht zu verstehen sein, da "syn-" ein griechisches Präfix ist, das "mit" oder "zusammen" bedeutet, und "chrono" die griechische Wurzel für "Zeit" ist. "Synchronisiert" bedeutet wörtlich "zur gleichen Zeit",
Grenzen
Beachten Sie, dass "Synchronisation" nicht unbedingt für die gesamte Lebensdauer eines oder beider Prozesse gilt. Ich würde argumentieren, dass es nur für "die Wartezeit bis einschließlich der Synchronisationspunkte" gilt. Somit können zwei Prozesse asynchron arbeiten, bis sie einen Zustand erreichen, in dem sie kommunizieren müssen. Dann werden sie synchronisiert, tauschen Informationen aus und fahren anschließend asynchron fort. Ein einfaches Beispiel ist, jemanden zum Kaffee zu treffen. Offensichtlich ist das Meeting ein Synchronisationspunkt (oder vielmehr viele), und die Tatsache, dass zwei Personen an diesem Punkt ankommen, zeigt die Synchronität. Wir würden das jedoch nicht sagen, weil sich zwei Menschen zum Kaffee trafen, diese beiden menschlichen Lebensind "synchronisiert". Vielleicht war dies der einzige Moment in ihrem Leben, den sie getroffen haben, und alles andere, was sie tun, ist ansonsten unabhängig.
Es ist auch nicht der Fall, dass zufällige Begegnungen Synchronität demonstrieren. Wenn sich zwei Fremde auf der Straße begegnen, ist die Tatsache, dass sie sich zu einem bestimmten Zeitpunkt an einem bestimmten Ort befinden, keine Synchronität. Auch nicht die Tatsache, dass eine Person auf einer Bank sitzt und auf den Bus wartet und eine andere zufällig vorbeigeht. Prozesse sind nur dann synchron, wenn sie sich zu einem bestimmten Zweck treffen .
Software-Verbindung
Lassen Sie uns nun über eine sehr grundlegende Aufgabe in der Software nachdenken: das Lesen aus einer Datei. Wie Sie wahrscheinlich wissen, ist der Massenspeicher normalerweise tausend- bis millionenfach langsamer als der Cache oder der Hauptspeicher. Aus diesem Grund bieten Betriebssysteme und Programmiersprachenbibliotheken im Allgemeinen sowohl synchrone als auch asynchrone E / A-Operationen an. Auch wenn Ihr Programm nur einen einzigen Thread hat, sollten Sie sich das Betriebssystem für die Zwecke dieser Diskussion als "separaten Prozess" vorstellen.
Synchronisieren
Wenn Sie einen "synchronen E / A-Lesevorgang" durchführen, muss Ihr Thread warten, bis die Daten verfügbar sind. An diesem Punkt wird der Vorgang fortgesetzt. Dies ähnelt einem Staffelläufer, der den Staffelstab an den nächsten Läufer abgibt. Stellen Sie sich stattdessen einen Staffellauf vor, bei dem nur zwei Läufer um die Strecke laufen und der zweite Läufer ebenfalls an den ersten Läufer zurückgibt.
In diesem Fall "passieren" der Programmthread und der OS-E / A-Prozess nicht gleichzeitig, weshalb es seltsam erscheint, zu sagen, dass diese Prozesse "synchronisiert" sind. Aber das ist die falsche Sichtweise! Das ist so, als würde man sagen: "Die Läufer eines Staffelteams laufen nicht zur gleichen Zeit, daher sind sie nicht synchronisiert." Tatsächlich sind beide Aussagen falsch! Die Läufer eines Staffelteams müssen und müssen zur gleichen Zeit laufen, aber nur zu einem ganz bestimmten Zeitpunkt: der Übergabe des Staffelstabs. Tatsächlich ist es nur dieser besondere Moment während des Rennens, der uns überzeugt, dass die Staffelteams von Anfang an synchronisiert sind! Wenn wir die E / A-Anforderung und die Antwort als "Taktstock" betrachten,
Wenn wir andererseits an etwas wie die Finite-Elemente-Analyse auf einem Supercomputer denken, müssen Tausende von Prozessen im Gleichschritt arbeiten, um einen massiven globalen Zustand zu aktualisieren. Selbst wenn einige der Knoten ihre Arbeit für einen bestimmten Zeitschritt vor anderen erledigen, müssen sie alle auf den Abschluss des Zeitschritts warten, da sich die Ergebnisse an die Nachbarn im Weltraum ausbreiten. Diese Art der Synchronisation ist wie bei den Glühwürmchen: Alle Akteure erledigen die gleiche Aufgabe.
Prozessvielfalt
Aus diesem Grund können wir einige Begriffe erfinden, um zu erkennen, dass drei Arten von Dingen vor sich gehen: "homogene Synchronität", "heterogene Synchronität" und "sequentielle Synchronität". Wenn also die Schauspieler dieselbe Aufgabe gleichzeitig ausführen (FEA, Glühwürmchen), sind sie "homogen". Wenn sie gleichzeitig verschiedene Aufgaben ausführen (Soldaten rennen oder kriechen oder schwimmen zu ihren Zielen, Physik oder Sound oder KI-Fäden in einem Spiel), sind sie "heterogen". Wenn sie Aufgaben einzeln ausführen, sind sie "sequentiell" (Relais-Läufer, blockierende E / A). Sie mögen sehr unterschiedlich aussehen, haben aber eine wesentliche Eigenschaft gemeinsam: Alle Arten von Akteuren führen einige Wartevorgänge durch, um sicherzustellen, dass alle zur gleichen Zeit am Synchronisationspunkt ankommen. zwischen Synchronisationspunkten oder "Ausführen der gleichen Aktion" ist für die Eigenschaft der Synchronität irrelevant.
Die Render-Pipelines in einer GPU sind synchron, da alle den Frame gemeinsam beenden und einen neuen Frame gemeinsam starten müssen. Sie sind homogen, weil sie die gleiche Arbeit leisten und alle gemeinsam aktiv sind. Die Hauptspielschleife eines Servers und die blockierenden E / A-Threads, die Remote-Eingaben verarbeiten, sind jedoch heterogen, da sie sehr unterschiedliche Aufgaben ausführen, und einige der E / A-Threads werden überhaupt nichts tun, weil nicht alle Die Verbindungen werden verwendet. Trotzdem sind sie synchronisiert, da sie den Status atomar teilen müssen (ein Spieler darf weder ein teilweises Update der Spielwelt sehen, noch darf der Server nur ein Fragment der Spielereingaben sehen).
Async
Betrachten wir nun einen "asynchronen E / A-Lesevorgang". Wenn Ihr Programm eine Anforderung an das Betriebssystem sendet, Daten aus dem Speicher zu lesen, wird der Aufruf sofort zurückgegeben . Lassen Sie uns Rückrufe ignorieren und uns auf das Polling konzentrieren. Im Allgemeinen entspricht der Moment, in dem Daten für Ihr Programm verfügbar sind, für den Thread Ihres Programms keinem bestimmten Zeitpunkt. Wenn Ihr Programm nicht explizit auf die Daten wartet, weiß der Thread nicht einmal genau, wann dieser Moment eintritt. Es wird nur festgestellt, dass die Daten bei der nächsten Überprüfung warten.
Es gibt keine spezielle Besprechungszeit, zu der das Betriebssystem und der Programmthread die Übergabe der Daten vereinbaren. Sie sind wie zwei Schiffe, die in der Nacht vorbeifahren. Die Asynchronität ist durch diese Abwesenheit von Wartezeiten gekennzeichnet. Natürlich wartet der Programmthread oft auf die E / A-Operation, aber das muss nicht sein. Während der E / A-Abruf ausgeführt wird, kann er problemlos weitere Berechnungen durchführen und erst später prüfen, ob noch etwas Zeit übrig ist. Sobald das Betriebssystem mit dem Abrufen von Daten fertig ist, wartet es natürlich auch nicht mehr. Es legt die Daten einfach an einem geeigneten Ort ab und erledigt das Geschäft. In diesem Fall gibt das Programm den Taktstock an das Betriebssystem weiter und das Betriebssystem kommt später wieder, wirft den Taktstock zusammen mit den Daten auf den Boden und verlässt die Spur. Das Programm kann warten oder nicht warten, um die Übergabe zu erhalten.
Parallelität
Wenn wir eine Funktion in Software als "asynchron" markieren, bedeutet dies oft, dass wir Parallelität wünschen . Denken Sie jedoch daran, dass Parallelität keine Synchronität impliziert . Die Glühwürmchen sind ein gutes Beispiel, weil auch sie synchrones und asynchrones Verhalten zeigten. Während die meisten Fliegen im Gleichschritt blitzten, stimmten viele offensichtlich nicht mit dem Rest der Gruppe überein und blitzten eher zufällig. Die Fliegen haben vielleicht gleichzeitig gewirkt , aber sie waren nicht alle synchronisiert .
Wenn wir nun einen Code als "asynchron" markieren, sieht das lustig aus, weil der Rest des Codes, der nicht so markiert ist, "synchron" ist. Was bedeutet das überhaupt? Bestanden wir nicht darauf, dass "Synchronisation" zwei zum Tango erforderte? Aber was ist, wenn es sich um Code handelt, der in einem einzelnen Thread ausgeführt wird? In diesem Fall müssen wir einen Schritt zurücktreten und uns ein Programm als eine Folge von Zuständen und Übergängen zwischen diesen Zuständen vorstellen. Eine Anweisung in einem Programm bewirkt einen Zustandsübergang. Wir können es uns als einen "Mikroprozess" vorstellen, der mit der Aussage beginnt und endet. Die durch die Sprache definierten Sequenzpunkte sind tatsächlich die Synchronisationspunkte dieser "Mikroprozesse". Und so können wir einen Single-Threaded anzeigen,
Die Integrität der Programmiersprache garantiert, dass Statusaktualisierungen nicht über Anweisungen hinweg stören, und die Sequenzpunkte definieren Grenzen, über die der Compiler keine beobachtbaren Optimierungen vornehmen darf. Beispielsweise kann die Reihenfolge der Auswertung von Ausdrücken in einer Anweisung undefiniert oder unterbestimmt sein, sodass der Compiler die Möglichkeit hat, die Anweisung auf verschiedene Arten zu optimieren. Zu Beginn der nächsten Anweisung sollte sich das Programm jedoch in einem genau definierten Zustand befinden, wenn der PL selbst einwandfrei ist.
Inzwischen sollte klar sein, was wir unter "asynchron" verstehen. Dies bedeutet genau, dass der implizite Synchronisationsvertrag innerhalb eines Codeblocks für den asynchronen Block ausgenommen ist. Es ist zulässig, den Programmstatus unabhängig zu aktualisieren, ohne die Sicherheitsgarantien zu erfüllen, die normalerweise durch das sequentielle (konsistente, synchrone) Rechenmodell impliziert werden. Das bedeutet natürlich, dass wir besonders darauf achten müssen, dass wir den Programmstatus nicht inkonsistent zerstören. Dies bedeutet normalerweise, dass wir eine eingeschränkte, explizite Synchronisation einführen, um mit dem asynchronen Block zu koordinieren. Beachten Sie, dass dies bedeutet, dass der asynchrone Block zu verschiedenen Zeiten sowohl asynchron als auch synchron sein kann! Wenn wir uns jedoch daran erinnern, dass die Synchronisation lediglich das Vorhandensein eines Synchronisationspunkts anzeigt, sollten wir keine Probleme haben, diesen Begriff zu akzeptieren.