Ich kam mit den beiden Konzepten ziemlich zufrieden, aber mit etwas, das mir nicht klar war.
Nachdem ich einige der Antworten gelesen habe, denke ich, dass ich eine korrekte und hilfreiche Metapher habe, um den Unterschied zu beschreiben.
Wenn Sie sich Ihre einzelnen Codezeilen als separate, aber geordnete Spielkarten vorstellen (halten Sie mich an, wenn ich erkläre, wie Lochkarten der alten Schule funktionieren), haben Sie für jede separate Prozedur einen eindeutigen Kartenstapel (nicht Kopieren & Einfügen!) und der Unterschied zwischen dem, was normalerweise passiert, wenn Code normal und asynchron ausgeführt wird, hängt davon ab, ob Sie sich darum kümmern oder nicht.
Wenn Sie den Code ausführen, übergeben Sie dem Betriebssystem eine Reihe von Einzeloperationen (in die Ihr Compiler oder Interpreter Ihren Code auf "höherer" Ebene unterteilt hat), die an den Prozessor übergeben werden sollen. Mit einem Prozessor kann jeweils nur eine Codezeile ausgeführt werden. Um die Illusion zu erzeugen, mehrere Prozesse gleichzeitig auszuführen, verwendet das Betriebssystem eine Technik, bei der der Prozessor nur wenige Zeilen von einem bestimmten Prozess gleichzeitig sendet und zwischen allen Prozessen wechselt, je nachdem, wie er es sieht passen. Das Ergebnis sind mehrere Prozesse, die dem Endbenutzer den Fortschritt zur gleichen Zeit anzeigen.
Für unsere Metapher besteht die Beziehung darin, dass das Betriebssystem die Karten immer mischt, bevor sie an den Prozessor gesendet werden. Wenn Ihr Kartenstapel nicht von einem anderen Stapel abhängt, bemerken Sie nicht, dass Ihr Stapel nicht mehr ausgewählt wurde, während ein anderer Stapel aktiv wurde. Also, wenn es dich nicht interessiert, ist es egal.
Wenn Sie sich jedoch darum kümmern (z. B. gibt es mehrere Prozesse - oder Stapel von Karten -, die voneinander abhängen), wird das Mischen des Betriebssystems Ihre Ergebnisse durcheinander bringen.
Das Schreiben von asynchronem Code erfordert die Behandlung der Abhängigkeiten zwischen der Ausführungsreihenfolge, unabhängig davon, wie diese Reihenfolge letztendlich lautet. Aus diesem Grund werden Konstrukte wie "Rückrufe" verwendet. Sie sagen dem Prozessor: "Als nächstes müssen Sie dem anderen Stapel mitteilen, was wir getan haben." Durch die Verwendung solcher Tools können Sie sicher sein, dass der andere Stapel benachrichtigt wird, bevor das Betriebssystem weitere Anweisungen ausführen kann. ("Wenn called_back == false: send (no_operation)" - nicht sicher, ob dies tatsächlich so implementiert ist, aber logischerweise denke ich, dass es konsistent ist.)
Bei parallelen Prozessen besteht der Unterschied darin, dass Sie zwei Stapel haben, die sich nicht umeinander kümmern, und zwei Mitarbeiter, die sie verarbeiten. Am Ende des Tages müssen Sie möglicherweise die Ergebnisse der beiden Stapel kombinieren, was dann eine Frage der Synchronität wäre, aber für die Ausführung ist es Ihnen egal.
Ich bin mir nicht sicher, ob dies hilft, aber ich finde immer mehrere Erklärungen hilfreich. Beachten Sie außerdem, dass die asynchrone Ausführung nicht auf einen einzelnen Computer und seine Prozessoren beschränkt ist. Im Allgemeinen handelt es sich um Zeit oder (noch allgemeiner) um eine Reihenfolge von Ereignissen. Wenn Sie also den abhängigen Stapel A an den Netzwerkknoten X und den gekoppelten Stapel B an Y senden, sollte der richtige asynchrone Code die Situation berücksichtigen können, als würde er lokal auf Ihrem Laptop ausgeführt.