Beste Antwort
Die Antwort auf die gestellte Frage lautet: Sie entspricht dem Unterschied zwischen „eins“ „und“ viele „.
Aber ich denke, was Sie wahrscheinlich wirklich gemeint haben, ist eher so: Wie unterscheiden sich Programmiermethoden für die Programmierung in einem einzelnen Thread in einem einzelnen Prozess von der in mehreren Threads in einem einzelnen Prozess?
Es wurden Bücher zu diesem Thema geschrieben. Aber ganz kurz, die Unterschiede beziehen sich alle auf einen Faktor: Wenn Sie in einem einzelnen Thread und Prozess programmieren, wissen Sie immer, wie Sie zur aktuellen Anweisung gekommen sind. Anweisungen erfolgen in einer einzigen Reihenfolge. Bei der Programmierung für viele Threads wissen Sie jedoch nicht, welche Anweisungen in welcher Reihenfolge ausgeführt wurden, um zur aktuellen Anweisung zu gelangen. Dies macht die Programmierung komplizierter.
Ein weiterer Faktor erschwert dies: Im Gegensatz zu Prozessen teilen sich Threads den Speicher. Sie wissen nicht, welcher Thread zuletzt einen bestimmten Speicherort berührt hat oder welcher der nächste sein wird, es sei denn, Sie haben eine Art „Synchronisation“. Daher das Schlüsselwort „synchronisiert“ in Java (von Ada abgezogen). Monitore, Semaphoren, Sperren Für die Synchronisierung wurden Bedingungsvariablen und sogar das Weiterleiten von Nachrichten verwendet.
Antwort
(Update: Achten Sie auf eine weitere Antwort, die besagt, dass Sie keinen Single-Threaded-Webserver haben können Behandelt gleichzeitige Anforderungen gut, da dies einfach nicht der Fall ist.)
Warum ist ein Multithread-Webserver besser als ein einzelner Thread-Server? Ist dies nicht.
Es gibt vier grundlegende Möglichkeiten, wie ein Webserver mit Parallelität umgehen kann:
- Forking Ein Betriebssystemprozess pro Anforderung (wie alte Versionen von Apache)
- , der einen Betriebssystem-Thread pro Anforderung erzeugt (wie eine neue Version von Apache)
- unter Verwendung einer Single-Threaded-Ereignisschleife (wie nginx) )
- mit grünen Fäden oder leichten Prozessen Geplant von einer VM-Laufzeit anstelle des Betriebssystems (wie in Erlang)
Derzeit sind die gängigsten Ansätze Nummer 2 und 3.
Beide haben Vor- und Nachteile von ihnen. Für E / A-gebundene -Operationen (ein Merkmal eines typischen Webservers) erhalten Sie bessere Leistung und höhere Anzahl gleichzeitiger Anforderungen , wenn Sie eine Single-Threaded-Ereignisschleife verwenden. Der Nachteil ist jedoch, dass Sie für alle Vorgänge ausschließlich asynchrone, nicht blockierende E / A verwenden müssen. Andernfalls blockieren Sie die Ereignisschleife und verlieren an Leistung. Aus diesem Grund ist es einfacher, einen Multithread-Server zu implementieren, aber Sie zahlen für die Leistung.
Für CPU-gebundene -Operationen (seltener für Ein gewöhnlicher Webserver (möglicherweise häufiger für eine rechenintensive API). Es ist am besten, einen Betriebssystem-Thread oder -Prozess pro Kern zu haben. Es ist einfach, Single-Threaded-Ereignisschleifen zu verwenden, da Sie einen Cluster aus mehreren Prozessen ausführen können, einen pro Kern. Es ist schwierig, mit Multithread-Servern zu arbeiten, denn wenn das Laichen von Threads Ihre einzige Möglichkeit ist, gleichzeitige Anforderungen zu verarbeiten, können Sie nicht wirklich steuern, wie viele Threads Sie haben werden, da Sie die Anzahl der Anforderungen nicht steuern. Sobald Sie mehr Threads als die Anzahl der CPU-Kerne haben, verlieren Sie die Leistung für Kontextschalter und Sie verwenden auch viel RAM.
Aus diesem Grund bietet ein Nginx-Server mit einem Thread eine bessere Leistung als ein Apache-Webserver mit mehreren Threads (und aus diesem Grund wurde Nginx überhaupt erst erstellt). Auch Redis , eine Datenbank mit außergewöhnlich hoher Leistung, ist Single-Threaded . P. >
Ein echtes Beispiel, das ich Ihnen geben kann, ist Folgendes: Mein erster Webserver war Apache, der auf einem Linux-Computer mit 500 MB RAM ausgeführt wurde. Es gab für jede Anforderung einen neuen Prozess heraus (es gab tatsächlich einen Pool, so dass nicht viel Forking erforderlich war, aber es musste diese Prozesse am Leben erhalten, um sie wiederzuverwenden und ab und zu zu beenden, um Ressourcenlecks zu vermeiden).
Mein Betriebssystem hat ungefähr 100 MB RAM verwendet. Jeder Apache-Prozess verwendete 20 MB RAM. Dies bedeutete, dass mein Server nur 20 gleichzeitige Anforderungen verarbeiten konnte und es keinen Weg daran vorbei gab, da ich keinen RAM mehr hatte. Die Prozesse waren größtenteils auf E / A blockiert, so dass die CPU-Auslastung sehr gering war, jede Anforderung über diesen 20 musste warten und wenn diese 20 z. Bei lang laufenden Downloads reagierte mein Server nicht mehr.
Bei der Einführung des Nginx-Webservers wurde eine Single-Threaded-Ereignisschleife verwendet und für keine Anforderung blockiert. Es könnte viel mehr gleichzeitige Anforderungen verarbeiten, ohne Probleme mit dem mythischen c10k-Problem zu haben. Nginx wurde im Grunde genommen entwickelt, um das c10k-Problem zu lösen (10.000 gleichzeitige Anforderungen).
Stellen Sie sich vor, wie viel RAM für 10.000 Threads verschwendet wird, wenn Sie dies tun könnte sogar so viele erzeugen und wie viel Zeit für Kontextwechsel verwendet wird.
Speichernutzung von Apache mit mehreren Threads im Vergleich zu Nginx mit einem Thread:
Dies ist übrigens der Grund, warum Ryan Dahl in Node.js eine nicht blockierende E / A- und eine Single-Threaded-Ereignisschleife verwendet hat und in Deno immer noch dieselbe Idee verwendet. denn auf diese Weise können Hochleistungs-Netzwerkserver geschrieben werden (im Gegensatz zu anderen Antworten hier).