Ist nicht blockierende E / A wirklich schneller als blockierende E / A mit mehreren Threads? Wie?


117

Ich habe im Internet nach technischen Details zum Blockieren von E / A und nicht blockierenden E / A gesucht und mehrere Personen gefunden, die angaben, dass nicht blockierende E / A schneller sind als blockierende E / A. Zum Beispiel in diesem Dokument .

Wenn ich blockierende E / A verwende, kann der aktuell blockierte Thread natürlich nichts anderes tun ... weil er blockiert ist. Sobald jedoch ein Thread blockiert wird, kann das Betriebssystem zu einem anderen Thread wechseln und erst wieder zurückwechseln, wenn für den blockierten Thread etwas zu tun ist. Solange es einen anderen Thread auf dem System gibt, der CPU benötigt und nicht blockiert ist, sollte im Vergleich zu einem ereignisbasierten nicht blockierenden Ansatz keine CPU-Leerlaufzeit mehr vorhanden sein.

Neben der Reduzierung der CPU-Leerlaufzeit sehe ich eine weitere Option, um die Anzahl der Aufgaben zu erhöhen, die ein Computer in einem bestimmten Zeitraum ausführen kann: Reduzieren Sie den durch das Wechseln von Threads verursachten Overhead. Aber wie geht das? Und ist der Overhead groß genug, um messbare Effekte zu zeigen? Hier ist eine Idee, wie ich mir vorstellen kann, wie es funktioniert:

  1. Um den Inhalt einer Datei zu laden, delegiert eine Anwendung diese Aufgabe an ein ereignisbasiertes E / A-Framework und übergibt eine Rückruffunktion zusammen mit einem Dateinamen
  2. Das Ereignisframework wird an das Betriebssystem delegiert, das einen DMA-Controller der Festplatte so programmiert, dass die Datei direkt in den Speicher geschrieben wird
  3. Das Ereignis-Framework ermöglicht die Ausführung von weiterem Code.
  4. Nach Abschluss der Disk-to-Memory-Kopie verursacht der DMA-Controller einen Interrupt.
  5. Der Interrupt-Handler des Betriebssystems benachrichtigt das ereignisbasierte E / A-Framework darüber, dass die Datei vollständig in den Speicher geladen wird. Wie macht es das? Mit einem Signal?
  6. Der Code, der derzeit im Ereignis-E / A-Framework ausgeführt wird, wird beendet.
  7. Das ereignisbasierte E / A-Framework überprüft seine Warteschlange, erkennt die Nachricht des Betriebssystems aus Schritt 5 und führt den in Schritt 1 erhaltenen Rückruf aus.

Funktioniert das so? Wenn nicht, wie funktioniert es? Das bedeutet, dass das Ereignissystem funktionieren kann, ohne jemals den Stapel explizit berühren zu müssen (z. B. ein echter Scheduler, der den Stapel sichern und den Stapel eines anderen Threads in den Speicher kopieren müsste, während er den Thread wechselt)? Wie viel Zeit spart dies tatsächlich? Ist da noch mehr dran?


5
kurze Antwort: Es geht mehr um den Aufwand, einen Thread pro Verbindung zu haben. Wenn Sie io nicht blockieren, können Sie vermeiden, dass ein Thread pro Verbindung vorhanden ist.
Dan D.

10
Das Blockieren von E / A ist auf einem System teuer, auf dem Sie nicht so viele Threads erstellen können, wie Verbindungen bestehen. Auf der JVM können Sie einige tausend Threads erstellen, aber was ist, wenn Sie über 100.000 Verbindungen haben? Sie müssen sich also an eine asynchrone Lösung halten. Es gibt jedoch Sprachen, in denen Threads nicht teuer sind (z. B. grüne Threads), wie in Go / Erlang / Rust, in denen es kein Problem ist, 100.000 Threads zu haben. Wenn die Anzahl der Threads groß sein kann, führt das Blockieren von E / A meiner Meinung nach zu schnelleren Antwortzeiten. Aber das müsste ich auch die Experten fragen, ob das in der Realität zutrifft.
OlliP

@OliverPlow, ich denke auch, weil das Blockieren von E / A normalerweise bedeutet, dass das System die "parallele Verwaltung" übernimmt, anstatt sie selbst mithilfe von Task-Warteschlangen und dergleichen durchzuführen.
Pacerier

1
@DanD., Und was ist, wenn der Overhead für Threads gleich dem Overhead für die Ausführung nicht blockierender E / A ist? (normalerweise wahr im Fall von grünen Fäden)
Pacerier

"Kopieren des Stapels" kommt nicht vor. Verschiedene Threads haben ihre Stapel an verschiedenen Adressen. Jeder Thread hat zusammen mit anderen Registern einen eigenen Stapelzeiger. Ein Kontextwechsel speichert / stellt nur den Architekturstatus (einschließlich aller Register) wieder her, jedoch nicht den Speicher. Zwischen Threads im selben Prozess muss der Kernel nicht einmal die Seitentabellen ändern.
Peter Cordes

Antworten:


44

Der größte Vorteil von nicht blockierenden oder asynchronen E / A besteht darin, dass Ihr Thread seine Arbeit parallel fortsetzen kann. Dies können Sie natürlich auch mit einem zusätzlichen Thread erreichen. Wie Sie für die beste Gesamtleistung (Systemleistung) angegeben haben, ist es wahrscheinlich besser, asynchrone E / A und nicht mehrere Threads zu verwenden (wodurch die Thread-Umschaltung verringert wird).

Schauen wir uns mögliche Implementierungen eines Netzwerkserverprogramms an, das 1000 parallel verbundene Clients verarbeiten soll:

  1. Ein Thread pro Verbindung (kann E / A blockieren, kann aber auch nicht blockierende E / A sein).
    Jeder Thread benötigt Speicherressourcen (auch Kernelspeicher!), Das ist ein Nachteil. Und jeder zusätzliche Thread bedeutet mehr Arbeit für den Scheduler.
  2. Ein Thread für alle Verbindungen.
    Dies entlastet das System, da wir weniger Threads haben. Es verhindert jedoch auch, dass Sie die volle Leistung Ihres Computers nutzen können, da Sie möglicherweise einen Prozessor zu 100% fahren und alle anderen Prozessoren im Leerlauf laufen lassen.
  3. Einige Threads, in denen jeder Thread einige der Verbindungen behandelt.
    Dies entlastet das System, da weniger Threads vorhanden sind. Und es können alle verfügbaren Prozessoren verwendet werden. Unter Windows wird dieser Ansatz von der Thread Pool API unterstützt .

Natürlich ist es per se kein Problem, mehr Threads zu haben. Wie Sie vielleicht erkannt haben, habe ich eine große Anzahl von Verbindungen / Threads ausgewählt. Ich bezweifle, dass Sie einen Unterschied zwischen den drei möglichen Implementierungen feststellen werden, wenn es sich nur um ein Dutzend Threads handelt (dies schlägt Raymond Chen auch im MSDN-Blogbeitrag vor. Hat Windows ein Limit von 2000 Threads pro Prozess? ).

Unter Windows bedeutet die Verwendung ungepufferter Datei-E / A , dass Schreibvorgänge eine Größe haben müssen, die ein Vielfaches der Seitengröße beträgt. Ich habe es nicht getestet, aber es scheint, dass dies auch die Schreibleistung für gepufferte synchrone und asynchrone Schreibvorgänge positiv beeinflussen könnte.

Die Schritte 1 bis 7, die Sie beschreiben, geben eine gute Vorstellung davon, wie es funktioniert. Unter Windows informiert Sie das Betriebssystem über den Abschluss einer asynchronen E / A ( WriteFilemit OVERLAPPEDStruktur) mithilfe eines Ereignisses oder eines Rückrufs. Rückruffunktionen werden beispielsweise nur aufgerufen, wenn Ihr Code WaitForMultipleObjectsExmit bAlertableset auf aufruft true.

Noch etwas im Internet lesen:


Aus der Sicht des Web deuten allgemein bekannte Kenntnisse (Internet, Kommentare von Experten) darauf hin, dass die max. Die Anzahl der Anforderungsthreads ist eine schlechte Sache beim Blockieren von E / A (wodurch die Verarbeitung von Anforderungen noch langsamer wird), da der Speicher erhöht und die Kontextwechselzeit erhöht wird. Tut Async IO jedoch nicht dasselbe, wenn der Job auf einen anderen Thread verschoben wird? Ja, Sie können jetzt mehr Anfragen bearbeiten, haben aber die gleiche Anzahl von Threads im Hintergrund. Was ist der wahre Vorteil davon?
JavierJ

1
@JavierJ Sie scheinen zu glauben, dass, wenn n Threads asynchrone Datei-E / A ausführen, weitere n Threads erstellt werden, um eine blockierende Datei-E / A auszuführen? Das ist nicht wahr. Das Betriebssystem unterstützt asynchrone Datei-E / A und muss nicht blockiert werden, wenn auf den Abschluss der E / A gewartet wird. Es kann E / A-Anforderungen in die Warteschlange stellen, und wenn ein Hardware-Interrupt (z. B. DMA) auftritt, kann es die Anforderung als erledigt markieren und ein Ereignis festlegen, das den Aufrufer-Thread signalisiert. Selbst wenn ein zusätzlicher Thread erforderlich wäre, könnte das Betriebssystem diesen Thread für mehrere E / A-Anforderungen von mehreren Threads verwenden.
Werner Henze

Vielen Dank, es ist sinnvoll, die Unterstützung für asynchrone E / A-Dateien des Betriebssystems einzubeziehen. Wenn ich jedoch Code für eine tatsächliche Implementierung (aus Web-Sicht) schreibe, z. B. mit Java Servlet 3.0 NIO, sehe ich immer noch einen Thread für die Anforderung und einen Hintergrund-Thread ( asynchrone Schleife zum Lesen einer Datei, Datenbank oder was auch immer.
JavierJ

1
@piyushGoyal Ich habe meine Antwort zurückgeschickt. Ich hoffe es ist jetzt klarer.
Werner Henze

1
Unter Windows bedeutet die Verwendung von asynchronen Datei-E / A, dass Schreibvorgänge eine Größe haben müssen, die ein Vielfaches der Seitengröße beträgt. - Nein, das tut es nicht. Sie denken an ungepufferte E / A. (Sie werden oft zusammen verwendet, müssen es aber nicht sein.)
Harry Johnston

29

E / A umfasst verschiedene Arten von Vorgängen wie das Lesen und Schreiben von Daten von Festplatten, den Zugriff auf Netzwerkressourcen, das Aufrufen von Webdiensten oder das Abrufen von Daten aus Datenbanken. Abhängig von der Plattform und der Art des Vorgangs nutzt die asynchrone E / A normalerweise Hardware- oder Low-Level-Systemunterstützung für die Ausführung des Vorgangs. Dies bedeutet, dass die CPU so wenig wie möglich belastet wird.

Auf Anwendungsebene verhindert asynchrone E / A, dass Threads auf den Abschluss von E / A-Vorgängen warten müssen. Sobald eine asynchrone E / A-Operation gestartet wird, gibt sie den Thread frei, auf dem sie gestartet wurde, und ein Rückruf wird registriert. Wenn der Vorgang abgeschlossen ist, wird der Rückruf zur Ausführung auf dem ersten verfügbaren Thread in die Warteschlange gestellt.

Wenn die E / A-Operation synchron ausgeführt wird, wird der laufende Thread so lange nicht ausgeführt, bis die Operation abgeschlossen ist. Die Laufzeit weiß nicht, wann der E / A-Vorgang abgeschlossen ist. Daher stellt sie dem wartenden Thread regelmäßig CPU-Zeit zur Verfügung. Diese CPU-Zeit könnte andernfalls von anderen Threads verwendet werden, für die tatsächlich CPU-gebundene Vorgänge ausgeführt werden müssen.

Wie @ user1629468 erwähnt, bietet asynchrone E / A keine bessere Leistung, sondern eine bessere Skalierbarkeit. Dies ist offensichtlich, wenn Sie in Kontexten ausgeführt werden, in denen nur eine begrenzte Anzahl von Threads verfügbar ist, wie dies bei Webanwendungen der Fall ist. Webanwendungen verwenden normalerweise einen Thread-Pool, aus dem sie jeder Anforderung Threads zuweisen. Wenn Anforderungen bei lang laufenden E / A-Vorgängen blockiert werden, besteht die Gefahr, dass der Webpool erschöpft wird und die Webanwendung einfriert oder nur langsam reagiert.

Eine Sache, die mir aufgefallen ist, ist, dass asynchrone E / A nicht die beste Option ist, wenn sehr schnelle E / A-Vorgänge ausgeführt werden. In diesem Fall ist der Vorteil, einen Thread nicht beschäftigt zu halten, während auf den Abschluss der E / A-Operation gewartet wird, nicht sehr wichtig, und die Tatsache, dass die Operation auf einem Thread gestartet und auf einem anderen abgeschlossen wird, erhöht die Gesamtausführung um einen Overhead.

Eine detailliertere Untersuchung, die ich kürzlich zum Thema asynchrone E / A vs. Multithreading durchgeführt habe, finden Sie hier .


Ich frage mich, ob es sich lohnen würde, zwischen E / A-Vorgängen zu unterscheiden, deren Abschluss erwartet wird, und Dingen, die möglicherweise nicht [z. B. "nächstes Zeichen, das an einer seriellen Schnittstelle ankommt" erhalten, in Fällen, in denen das Remote-Gerät möglicherweise oder möglicherweise nicht etwas senden]. Wenn erwartet wird, dass eine E / A-Operation innerhalb einer angemessenen Zeit abgeschlossen wird, kann die Bereinigung der zugehörigen Ressourcen verzögert werden, bis die Operation abgeschlossen ist. Sollte der Vorgang jedoch niemals abgeschlossen werden, wäre eine solche Verzögerung nicht zumutbar.
Superkatze

@supercat Das von Ihnen beschriebene Szenario wird in Anwendungen und Bibliotheken auf niedrigerer Ebene verwendet. Server verlassen sich darauf, da sie ständig auf eingehende Verbindungen warten. Asynchrone E / A wie oben beschrieben können nicht in dieses Szenario passen, da sie darauf basieren, einen bestimmten Vorgang zu starten und einen Rückruf für dessen Abschluss zu registrieren. In dem von Ihnen beschriebenen Fall müssen Sie einen Rückruf für ein Systemereignis registrieren und jede Benachrichtigung verarbeiten. Sie verarbeiten kontinuierlich Eingaben, anstatt Vorgänge auszuführen. Wie gesagt, dies geschieht normalerweise auf niedriger Ebene, fast nie in Ihren Apps.
Florin Dumitrescu

Das Muster ist bei Anwendungen mit verschiedenen Hardwaretypen weit verbreitet. Serielle Ports sind nicht mehr so ​​häufig wie früher, aber USB-Chips, die serielle Ports emulieren, sind bei der Entwicklung spezieller Hardware sehr beliebt. Zeichen aus solchen Dingen werden auf Anwendungsebene behandelt, da das Betriebssystem nicht wissen kann, dass eine Folge von Eingabezeichen bedeutet, dass beispielsweise eine Kassenschublade geöffnet wurde und eine Benachrichtigung irgendwo gesendet werden sollte.
Supercat

Ich denke nicht, dass der Teil über die CPU-Kosten für das Blockieren von E / A korrekt ist: Im blockierenden Zustand wird ein Thread, der das Blockieren von E / A ausgelöst hat, vom Betriebssystem auf Wartezeit gestellt und kostet keine CPU-Zeiträume, bis das E / A vollständig abgeschlossen ist Setzt das Betriebssystem (Benachrichtigungen durch Interrupts) den blockierten Thread fort? Was Sie beschrieben haben (beschäftigtes Warten durch langes Abrufen), ist nicht, wie das Blockieren von E / A in fast jeder Laufzeit / jedem Compiler implementiert ist.
Lifu Huang

4

Der Hauptgrund für die Verwendung von AIO liegt in der Skalierbarkeit. Im Kontext einiger Themen sind die Vorteile nicht offensichtlich. Wenn das System jedoch auf 1000 Threads skaliert, bietet AIO eine viel bessere Leistung. Die Einschränkung ist, dass die AIO-Bibliothek keine weiteren Engpässe verursachen sollte.


4

Um eine Geschwindigkeitsverbesserung aufgrund einer Form von Multi-Computing zu vermuten, müssen Sie entweder davon ausgehen, dass mehrere CPU-basierte Aufgaben gleichzeitig auf mehreren Computerressourcen (im Allgemeinen Prozessorkerne) ausgeführt werden oder dass nicht alle Aufgaben auf der gleichzeitigen Verwendung von basieren Dieselbe Ressource - das heißt, einige Aufgaben hängen möglicherweise von einer Systemunterkomponente ab (z. B. Festplattenspeicher), während einige Aufgaben von einer anderen abhängen (Empfang von Kommunikation von einem Peripheriegerät), und andere erfordern möglicherweise die Verwendung von Prozessorkernen.

Das erste Szenario wird oft als "parallele" Programmierung bezeichnet. Das zweite Szenario wird häufig als "gleichzeitige" oder "asynchrone" Programmierung bezeichnet, obwohl "gleichzeitig" manchmal auch verwendet wird, um den Fall zu bezeichnen, dass ein Betriebssystem lediglich die Ausführung mehrerer Aufgaben verschachteln kann, unabhängig davon, ob eine solche Ausführung erforderlich ist seriell platzieren oder wenn mehrere Ressourcen verwendet werden können, um eine parallele Ausführung zu erreichen. In diesem letzteren Fall bezieht sich "gleichzeitig" im Allgemeinen auf die Art und Weise, wie die Ausführung in das Programm geschrieben wird, und nicht auf die Perspektive der tatsächlichen Gleichzeitigkeit der Aufgabenausführung.

Es ist sehr einfach, mit stillschweigenden Annahmen darüber zu sprechen. Einige behaupten beispielsweise schnell: "Asynchrone E / A sind schneller als Multithread-E / A." Diese Behauptung ist aus mehreren Gründen zweifelhaft. Erstens könnte es vorkommen, dass einige gegebene asynchrone E / A-Frameworks genau mit Multithreading implementiert werden. In diesem Fall sind sie ein und dasselbe und es macht keinen Sinn zu sagen, dass ein Konzept "schneller als" das andere ist .

Zweitens müssen Sie auch dann, wenn eine Single-Threaded-Implementierung eines asynchronen Frameworks (z. B. einer Single-Threaded-Ereignisschleife) vorhanden ist, eine Annahme darüber treffen, was diese Schleife tut. Eine dumme Sache, die Sie mit einer Single-Threaded-Ereignisschleife tun können, ist beispielsweise die Anforderung, zwei verschiedene rein CPU-gebundene Aufgaben asynchron auszuführen. Wenn Sie dies auf einem Computer mit nur einem idealisierten Einzelprozessorkern tun würden (ohne Berücksichtigung moderner Hardwareoptimierungen), würde die Ausführung dieser Aufgabe "asynchron" nicht anders ablaufen als mit zwei unabhängig verwalteten Threads oder mit nur einem einzigen Prozess. - Der Unterschied kann auf das Umschalten des Thread-Kontexts oder die Optimierung des Betriebssystemplans zurückzuführen sein. Wenn jedoch beide Aufgaben an die CPU gehen, ist dies in beiden Fällen ähnlich.

Es ist nützlich, sich viele der ungewöhnlichen oder dummen Eckfälle vorzustellen, auf die Sie stoßen könnten.

"Asynchron" muss nicht gleichzeitig sein, zum Beispiel wie oben: Sie "asynchron" führen zwei CPU-gebundene Tasks auf einem Computer mit genau einem Prozessorkern aus.

Die Ausführung mit mehreren Threads muss nicht gleichzeitig erfolgen: Sie erzeugen zwei Threads auf einem Computer mit einem einzelnen Prozessorkern oder fordern zwei Threads auf, eine andere Art von knapper Ressource zu erwerben (stellen Sie sich beispielsweise eine Netzwerkdatenbank vor, die nur einen einrichten kann Verbindung zu einem Zeitpunkt). Die Ausführung der Threads kann verschachtelt sein, wie es der Scheduler des Betriebssystems für richtig hält, aber ihre Gesamtlaufzeit kann nicht auf einem einzelnen Kern reduziert werden (und wird durch das Wechseln des Thread-Kontexts erhöht) (oder allgemeiner, wenn Sie mehr Threads erzeugen als vorhanden Kerne, um sie auszuführen, oder mehr Threads, die nach einer Ressource fragen, als die Ressource aushalten kann). Das Gleiche gilt auch für die Mehrfachverarbeitung.

Daher müssen weder asynchrone E / A noch Multithreading einen Leistungsgewinn in Bezug auf die Laufzeit bieten. Sie können sogar die Dinge verlangsamen.

Wenn Sie jedoch einen bestimmten Anwendungsfall definieren, z. B. ein bestimmtes Programm, das sowohl einen Netzwerkaufruf zum Abrufen von Daten von einer mit dem Netzwerk verbundenen Ressource wie einer entfernten Datenbank ausführt als auch eine lokale CPU-gebundene Berechnung durchführt, können Sie anfangen, darüber nachzudenken Die Leistungsunterschiede zwischen den beiden Methoden unter einer bestimmten Annahme über die Hardware.

Die zu stellenden Fragen: Wie viele Rechenschritte muss ich ausführen und wie viele unabhängige Ressourcensysteme gibt es, um sie auszuführen? Gibt es Teilmengen der Rechenschritte, die die Verwendung unabhängiger Systemunterkomponenten erfordern und davon gleichzeitig profitieren können? Wie viele Prozessorkerne habe ich und wie hoch ist der Aufwand für die Verwendung mehrerer Prozessoren oder Threads, um Aufgaben auf separaten Kernen auszuführen?

Wenn Ihre Aufgaben weitgehend von unabhängigen Subsystemen abhängen, ist eine asynchrone Lösung möglicherweise hilfreich. Wenn die Anzahl der für die Verarbeitung erforderlichen Threads groß wäre, sodass die Kontextumschaltung für das Betriebssystem nicht mehr trivial wäre, ist eine asynchrone Lösung mit einem Thread möglicherweise besser.

Immer wenn die Aufgaben an dieselbe Ressource gebunden sind (z. B. müssen mehrere gleichzeitig auf dasselbe Netzwerk oder dieselbe lokale Ressource zugreifen), führt Multithreading wahrscheinlich zu einem unbefriedigenden Overhead, und während Single-Threaded-Asynchronität in einer solchen Ressource möglicherweise weniger Overhead verursacht. begrenzte Situation kann es auch keine Beschleunigung erzeugen. In einem solchen Fall besteht die einzige Option (wenn Sie eine Beschleunigung wünschen) darin, mehrere Kopien dieser Ressource verfügbar zu machen (z. B. mehrere Prozessorkerne, wenn die knappe Ressource CPU ist; eine bessere Datenbank, die mehr gleichzeitige Verbindungen unterstützt, wenn die knappe Ressource vorhanden ist ist eine verbindungsbeschränkte Datenbank usw.).

Eine andere Möglichkeit ist: Das Betriebssystem kann die Verwendung einer einzelnen Ressource für zwei Aufgaben verschachteln. Dies kann nicht schneller sein, als nur eine Aufgabe die Ressource verwenden zu lassen, während die andere wartet, und dann die zweite Aufgabe seriell zu beenden. Darüber hinaus bedeuten die Scheduler-Kosten für das Interleaving, dass in jeder realen Situation tatsächlich eine Verlangsamung auftritt. Es spielt keine Rolle, ob die verschachtelte Nutzung der CPU, einer Netzwerkressource, einer Speicherressource, eines Peripheriegeräts oder einer anderen Systemressource erfolgt.


2

Eine mögliche Implementierung von nicht blockierenden E / A ist genau das, was Sie gesagt haben, mit einem Pool von Hintergrund-Threads, die E / A blockieren und den Thread des Absenders über einen Rückrufmechanismus über die E / A benachrichtigen. So funktioniert das AIO- Modul in glibc. Hier sind einige vage Details zur Implementierung.

Während dies eine gute Lösung ist, die ziemlich portabel ist (solange Sie Threads haben), kann das Betriebssystem in der Regel nicht blockierende E / A effizienter warten. Dieser Wikipedia-Artikel listet mögliche Implementierungen neben dem Thread-Pool auf.


2

Ich bin derzeit dabei, Async io auf einer eingebetteten Plattform mithilfe von Protothreads zu implementieren. Das nicht blockierende io macht den Unterschied zwischen 16000 fps und 160 fps. Der größte Vorteil von nicht blockierendem Io ist, dass Sie Ihren Code so strukturieren können, dass er andere Aufgaben ausführt, während die Hardware ihre Aufgabe erfüllt. Auch die Initialisierung von Geräten kann parallel erfolgen.

Martin


1

In Node werden mehrere Threads gestartet, dies ist jedoch eine Schicht in der C ++ - Laufzeit.

"Also ja, NodeJS ist Single-Threaded, aber das ist eine halbe Wahrheit, tatsächlich ist es ereignisgesteuert und Single-Threaded mit Hintergrundarbeitern. Die Hauptereignisschleife ist Single-Threaded, aber die meisten E / A-Arbeiten werden auf separaten Threads ausgeführt. weil die E / A-APIs in Node.js von Natur aus asynchron / nicht blockierend sind, um die Ereignisschleife aufzunehmen. "

https://codeburst.io/how-node-js-single-thread-mechanism-work-understanding-event-loop-in-nodejs-230f7440b0ea

"Node.js ist nicht blockierend, was bedeutet, dass alle Funktionen (Rückrufe) an die Ereignisschleife delegiert werden und von verschiedenen Threads ausgeführt werden (oder ausgeführt werden können). Dies wird von der Laufzeit von Node.js verwaltet."

https://itnext.io/multi-threading-and-multi-process-in-node-js-ffa5bb5cde98 

Die Erklärung "Knoten ist schneller, weil er nicht blockiert ..." ist ein bisschen Marketing und das ist eine großartige Frage. Es ist effizient und skalierbar, aber nicht genau Single-Threaded.


0

Soweit ich weiß, besteht die Verbesserung darin, dass asynchrone E / A die sogenannten E / A-Abschlussports verwendet (ich spreche von MS System, nur um dies zu verdeutlichen) . Durch die Verwendung des asynchronen Aufrufs nutzt das Framework diese Architektur automatisch, und dies soll wesentlich effizienter sein als der Standard-Threading-Mechanismus. Als persönliche Erfahrung kann ich sagen, dass Sie Ihre Anwendung vernünftigerweise reaktiver fühlen würden, wenn Sie AsyncCalls bevorzugen, anstatt Threads zu blockieren.


0

Lassen Sie mich ein Gegenbeispiel geben, dass asynchrone E / A nicht funktionieren. Ich schreibe einen Proxy ähnlich dem unten verwendeten Boost :: Asio. https://github.com/ArashPartow/proxy/blob/master/tcpproxy_server.cpp

Das Szenario in meinem Fall ist jedoch, dass eingehende (von der Clientseite) Nachrichten schnell sind, während ausgehende (zur Serverseite) für eine Sitzung langsam sind, um mit der eingehenden Geschwindigkeit Schritt zu halten oder den gesamten Proxy-Durchsatz zu maximieren, den wir verwenden müssen mehrere Sitzungen unter einer Verbindung.

Somit funktioniert dieses asynchrone E / A-Framework nicht mehr. Wir benötigen einen Thread-Pool, um ihn an den Server zu senden, indem wir jedem Thread eine Sitzung zuweisen.

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.