Was sind die Unterschiede zwischen einer "Coroutine" und einem "Thread"?
Antworten
Zu viele Anzeigen?Erstes Lesen: Gleichzeitigkeit vs. Parallelität - Was ist der Unterschied?
Gleichzeitigkeit ist die Trennung von Aufgaben Ausführung. Parallelität ist die gleichzeitige Ausführung von mehreren von Aufgaben, um die Geschwindigkeit zu erhöhen. - https://github.com/servo/servo/wiki/Design
Kurze Antwort: Bei Threads schaltet das Betriebssystem die laufenden Threads entsprechend seinem Scheduler, einem Algorithmus im Betriebssystemkern, präemptiv um. Bei Coroutines bestimmen der Programmierer und die Programmiersprache, wann Coroutines umgeschaltet werden; mit anderen Worten: Aufgaben werden kooperativ multitaskingfähig gemacht, indem Funktionen an bestimmten Punkten angehalten und wieder aufgenommen werden, typischerweise (aber nicht unbedingt) innerhalb eines einzelnen Threads.
Lange Antwort: Im Gegensatz zu Threads, die vom Betriebssystem im Voraus geplant werden, sind Coroutine-Switches kooperativ, d. h. der Programmierer (und möglicherweise die Programmiersprache und ihre Laufzeit) bestimmt, wann ein Switch stattfindet.
Im Gegensatz zu Threads, die präemptiv sind, sind co kooperativ (der Programmierer bestimmt, wann ein Wechsel stattfindet). Der Kernel - http://www.boost.org/doc/libs/1_55_0/libs/coroutine/doc/html/coroutine/overview.html
Eine Sprache, die Folgendes unterstützt einheimische Fäden kann seine Threads (Benutzer-Threads) auf den Threads des Betriebssystems ausführen ( Kernel-Threads ). Jeder Prozess hat mindestens einen Kernel-Thread. Kernel-Threads sind wie Prozesse, mit dem Unterschied, dass sie den Speicherplatz in ihrem eigenen Prozess mit allen anderen Threads in diesem Prozess teilen. Ein Prozess "besitzt" alle ihm zugewiesenen Ressourcen, wie Speicher, Datei-Handles, Sockets, Geräte-Handles usw., und diese Ressourcen werden alle von seinen Kernel-Threads gemeinsam genutzt.
Der Scheduler des Betriebssystems ist Teil des Kernels, der jeden Thread für eine bestimmte Zeit laufen lässt (auf einer Maschine mit einem Prozessor). Der Scheduler weist jedem Thread Zeit zu (Timeslicing), und wenn der Thread innerhalb dieser Zeit nicht fertig ist, unterbricht er ihn und schaltet auf einen anderen Thread um. Auf einem Multiprozessor-Rechner können mehrere Threads parallel laufen, da jeder Thread auf einem separaten Prozessor geplant werden kann (aber nicht unbedingt muss).
Auf einem Einzelprozessor-Rechner werden Threads in Zeitscheiben eingeteilt und schnell umgeschaltet (unter Linux beträgt die Standard-Zeitscheibe 100 ms), so dass sie gleichzeitig ablaufen. Sie können jedoch nicht parallel (gleichzeitig) ausgeführt werden, da ein Single-Core-Prozessor nur eine Sache zur gleichen Zeit ausführen kann.
Koroutinen und/oder Stromerzeuger kann zur Umsetzung kooperativer Funktionen verwendet werden. Anstatt in Kernel-Threads ausgeführt und vom Betriebssystem geplant zu werden, laufen sie in einem einzigen Thread, bis sie nachgeben oder beendet werden, wobei sie nach Belieben an andere Funktionen übergeben werden. Sprachen mit Stromerzeuger wie Python und ECMAScript 6, können zur Erstellung von Coroutines verwendet werden. Async/await (zu finden in C#, Python, ECMAscript 7, Rust) ist eine Abstraktion, die auf Generatorfunktionen aufbaut, die Futures/Promises liefern.
In manchen Kontexten, Koroutinen kann sich auf Stapelfunktionen beziehen, während Stromerzeuger kann sich auf stapellose Funktionen beziehen.
Fasern , leichte Fäden und grüne Fäden sind andere Namen für Coroutines oder Coroutine-ähnliche Dinge. Sie sehen manchmal (in der Regel absichtlich) eher wie Betriebssystem-Threads in der Programmiersprache aus, aber sie laufen nicht parallel wie echte Threads und funktionieren stattdessen wie Coroutines. (Je nach Sprache oder Implementierung kann es weitere technische Besonderheiten oder Unterschiede zwischen diesen Konzepten geben).
Zum Beispiel hatte Java " grüne Fäden "Es handelte sich um Threads, die von der virtuellen Java-Maschine (JVM) und nicht von den Kernel-Threads des zugrunde liegenden Betriebssystems geplant wurden. Sie liefen nicht parallel und nutzten auch nicht die Vorteile mehrerer Prozessoren/Kerne, da dies einen nativen Thread erfordern würde! Da sie nicht vom Betriebssystem geplant wurden, waren sie mehr wie Coroutines als Kernel-Threads. Grüne Threads wurden von Java verwendet, bis native Threads in Java 1.2 eingeführt wurden.
Threads verbrauchen Ressourcen. In der JVM hat jeder Thread seinen eigenen Stack, der normalerweise 1 MB groß ist. 64k ist die kleinste Menge an Stack-Speicherplatz, die pro Thread in der JVM erlaubt ist. Die Größe des Thread-Stacks kann in der Befehlszeile der JVM konfiguriert werden. Trotz des Namens sind Threads nicht kostenlos, da sie Ressourcen verbrauchen, z. B. benötigt jeder Thread seinen eigenen Stack, thread-lokalen Speicher (falls vorhanden) und die Kosten für Thread-Scheduling/Kontextwechsel/CPU-Cache-Invalidierung. Dies ist einer der Gründe, warum Coroutines für leistungsrelevante, hoch-konkurrierende Anwendungen so beliebt geworden sind.
Mac OS erlaubt einem Prozess nur die Zuweisung von etwa 2000 Threads, und Linux weist jedem Thread 8 MB Stack zu und erlaubt nur so viele Threads, wie in den physischen Arbeitsspeicher passen.
Daher sind Threads am schwersten (in Bezug auf Speicherverbrauch und Kontextwechselzeit), dann folgen Coroutines und schließlich sind Generatoren am leichtesten.
Etwa 7 Jahre zu spät, aber den Antworten hier fehlt etwas Kontext zu Co-Routinen vs. Threads. Warum sind Koroutinen in letzter Zeit so viel Aufmerksamkeit erhalten, und wann würde ich sie im Vergleich zu Gewinde ?
Zunächst einmal, wenn Coroutines laufen gleichzeitig (niemals in parallel ), warum sollte man sie den Fäden vorziehen?
Die Antwort ist, dass Koroutinen eine sehr hohes Maß an Gleichzeitigkeit mit sehr geringe Gemeinkosten . In einer Thread-Umgebung gibt es in der Regel höchstens 30-50 Threads, bevor die Menge an Overhead, die für die Planung dieser Threads (durch den System-Scheduler) verschwendet wird deutlich verkürzt die Zeit, in der die Threads tatsächlich nützliche Arbeit leisten.
Ok, also mit Threads kann man Parallelität haben, aber nicht zu viel Parallelität, ist das nicht immer noch besser als eine Co-Routine, die in einem einzigen Thread läuft? Nun, nicht unbedingt. Denken Sie daran, dass eine Co-Routine immer noch Parallelität ohne Scheduler-Overhead erreichen kann - sie verwaltet einfach die Kontextumschaltung selbst.
Wenn Sie beispielsweise eine Routine haben, die eine bestimmte Arbeit ausführt und eine Operation durchführt, von der Sie wissen, dass sie für einige Zeit blockiert wird (z. B. eine Netzwerkanfrage), können Sie mit einer Co-Routine sofort zu einer anderen Routine wechseln, ohne dass Sie den Systemplaner in diese Entscheidung einbeziehen müssen - ja, Sie als Programmierer muss angeben, wann Co-Routinen wechseln können.
Mit einer Vielzahl von Routinen, die sehr kleine Teile der Arbeit erledigen und freiwillig zwischen ihnen wechseln, haben Sie ein Effizienzniveau erreicht, das kein Scheduler jemals erreichen könnte. Sie können nun Tausende von Coroutines zusammenarbeiten lassen, im Gegensatz zu einigen Dutzend Threads.
Da Ihre Routinen nun an vorher festgelegten Punkten zwischen einander wechseln, können Sie nun auch Sperren vermeiden bei gemeinsam genutzten Datenstrukturen (weil Sie Ihrem Code niemals sagen würden, dass er mitten in einem kritischen Abschnitt zu einer anderen Coroutine wechseln soll)
Ein weiterer Vorteil ist der wesentlich geringere Speicherbedarf. Beim threaded-Modell muss jeder Thread seinen eigenen Stack zuweisen, so dass der Speicherverbrauch linear mit der Anzahl der Threads wächst. Bei Co-Routinen steht die Anzahl der Routinen nicht in direktem Zusammenhang mit dem Speicherverbrauch.
Und schließlich wird Co-Routinen viel Aufmerksamkeit geschenkt, weil in einigen Programmiersprachen (wie Python) Ihre Threads können ohnehin nicht parallel laufen - Sie laufen gleichzeitig ab, genau wie Coroutines, aber ohne den geringen Speicher- und Free Scheduling-Overhead.
Koroutinen sind eine Form der sequentiellen Verarbeitung: es wird immer nur eine ausgeführt (genau wie Unterprogramme AKA Prozeduren AKA Funktionen - sie geben nur den Staffelstab untereinander flüssiger weiter).
Threads sind (zumindest konzeptionell) eine Form der gleichzeitigen Verarbeitung: mehrere Threads können zu einem bestimmten Zeitpunkt ausgeführt werden. (Traditionell wurde diese Gleichzeitigkeit auf Ein-CPU- und Ein-Kern-Rechnern mit Hilfe des Betriebssystems simuliert - heutzutage, da so viele Rechner über mehrere CPUs und/oder Kerne verfügen, werden Threads de facto gleichzeitig ausgeführt werden, nicht nur "konzeptionell").
12 Jahre zu spät zur Diskussion, aber eine Coroutine hat die Erklärung im Namen. Coroutine kann in Co und Routine zerlegt werden.
Eine Routine ist in diesem Zusammenhang nur eine Abfolge von Operationen/Aktionen, und bei der Ausführung/Verarbeitung einer Routine wird die Abfolge der Operationen eine nach der anderen in genau der gleichen Reihenfolge wie angegeben ausgeführt.
Co steht für Zusammenarbeit. Von einer Co-Routine wird verlangt (oder besser erwartet), dass sie ihre Ausführung bereitwillig unterbricht, um anderen Co-Routinen die Chance zu geben, ebenfalls ausgeführt zu werden. Bei einer Co-Routine geht es also darum, CPU-Ressourcen (bereitwillig) zu teilen, damit andere dieselbe Ressource nutzen können, die man selbst nutzt.
Ein Thread hingegen braucht seine Ausführung nicht zu unterbrechen. Das Aussetzen ist für den Thread völlig transparent, und der Thread wird von der zugrunde liegenden Hardware gezwungen, sich selbst auszusetzen. Dies geschieht auch auf eine Art und Weise, die für den Thread weitgehend transparent ist, da er nicht benachrichtigt wird und sein Zustand nicht verändert, sondern gespeichert und später wiederhergestellt wird, wenn der Thread fortgesetzt werden darf.
Eine Sache, die nicht stimmt, ist, dass Co-Routinen nicht gleichzeitig ausgeführt werden können und keine Race Conditions auftreten können. Es hängt von dem System ab, auf dem die Co-Routinen laufen, und es ist leicht möglich, Co-Routinen abzubilden.
Es spielt keine Rolle, wie sich die Co-Routinen selbst aufhängen. Zurück in Windows 3.1 int 03 wurde in alle Programme gewebt (oder musste dort platziert werden) und in C # fügen wir Ertrag.
Mit einem Wort: Vorkaufsrecht. Coroutines verhalten sich wie Jongleure, die sich gegenseitig immer wieder einen gut einstudierten Punkt zuweisen. Threads (echte Threads) können an fast jedem Punkt unterbrochen und später wieder fortgesetzt werden. Das bringt natürlich alle möglichen Probleme mit Ressourcenkonflikten mit sich, daher Pythons berüchtigtes GIL - Global Interpreter Lock.
Viele Thread-Implementierungen sind eigentlich eher Coroutines.
- See previous answers
- Weitere Antworten anzeigen