Was können mehrere Threads tun, was ein einzelner Thread nicht kann? [geschlossen]

100

Während Threads die Ausführung von Code beschleunigen können, werden sie tatsächlich benötigt? Kann jeder Code mit einem einzelnen Thread erstellt werden oder gibt es etwas, das nur mit mehreren Threads erreicht werden kann?

Wütender Vogel
quelle
29
Aufgrund einiger der hier bereits geposteten Antworten halte ich eine Klarstellung für angebracht. Der Zweck von Threading besteht darin, es Computern zu ermöglichen, mehr als eine Sache gleichzeitig zu tun (anscheinend). Wenn dies mit einem Computer mit einem einzelnen Kern durchgeführt wird, sehen Sie keine allgemeine Beschleunigung. Sie werden sehen, dass die Blockierung aufgehoben wird. In einem Singlethread-Programm wird die Benutzeroberfläche blockiert, während Berechnungen ausgeführt werden. In einem Multithread-Programm kann die Benutzeroberfläche möglicherweise weiterhin verwendet werden, während die Berechnungen im Hintergrund ausgeführt werden.
Robert Harvey
11
Wenn Sie auf Ihrem Computer mehrere Prozessorkerne haben (was heutzutage üblich ist), kann Ihr Programm Threads verwenden, um zusätzliche Kerne für die Verarbeitung zu nutzen, und Sie werden eine Beschleunigung feststellen, da Sie mehr Leistung aufwenden Ihr Computerproblem. Wenn Sie überhaupt keine Threads verwenden, verwendet Ihr Programm nur einen einzelnen Prozessorkern. Dies ist in Ordnung, wenn dies alles ist, was Sie benötigen.
Robert Harvey
14
Ein Single-Thread-Programm kann Ressourcen nicht wie ein Multi-Thread-Programm blockieren.
zzzzBov
3
Antwort: laufen auf zwei Kernen
Daniel Little
6
Antwort: einen Deadlock verursachen.
Job

Antworten:

111

Erstens können Threads die Ausführung von Code nicht beschleunigen. Sie sorgen nicht dafür, dass der Computer schneller läuft. Sie können lediglich die Effizienz des Computers steigern, indem sie Zeit verwenden, die sonst verschwendet würde. Bei bestimmten Verarbeitungsarten kann diese Optimierung die Effizienz erhöhen und die Laufzeit verkürzen.

Die einfache Antwort lautet ja. Sie können jeden Code schreiben, der auf einem einzelnen Thread ausgeführt werden soll. Beweis: Ein Einzelprozessorsystem kann Befehle möglicherweise nur linear ausführen. Wenn das Betriebssystem mehrere Ausführungszeilen hat, werden die Interrupts verarbeitet, der Status des aktuellen Threads wird gespeichert und ein weiterer Thread wird gestartet.

Die komplexe Antwort ist ... komplexer! Der Grund, warum Multithread-Programme häufig effizienter sind als lineare, liegt an einem Hardware- "Problem". Die CPU kann Berechnungen schneller ausführen als Speicher- und Festplatten-E / A. So wird beispielsweise ein "add" -Befehl viel schneller ausgeführt als ein "fetch". Caches und spezielles Abrufen von Programmbefehlen (der genaue Begriff ist hier nicht bekannt) können dies in gewissem Maße verhindern, aber das Geschwindigkeitsproblem bleibt bestehen.

Threading ist eine Möglichkeit, diese Nichtübereinstimmung zu beheben, indem die CPU für CPU-gebundene Anweisungen verwendet wird, während die E / A-Anweisungen ausgeführt werden. Ein typischer Thread-Ausführungsplan wäre wahrscheinlich: Daten abrufen, Daten verarbeiten, Daten schreiben. Angenommen, das Abrufen und Schreiben dauert 3 Zyklen, und die Verarbeitung dauert zur Veranschaulichung einen. Sie können sehen, dass der Computer während des Lese- oder Schreibvorgangs zwei Zyklen lang nichts tut ? Klar ist es faul und wir müssen unsere Optimierungspeitsche knacken!

Wir können den Prozess mit Threading umschreiben, um diese verschwendete Zeit zu nutzen:

  1. # 1 holen
  2. keine Operation
  3. # 2 holen
  4. # 1 ist fertig, verarbeite es
  5. schreibe # 1
  6. # 1 holen
  7. # 2 ist fertig, verarbeite es
  8. schreibe # 2
  9. holen Sie # 2

Und so weiter. Natürlich ist dies ein etwas ausgeklügeltes Beispiel, aber Sie können sehen, wie diese Technik die Zeit nutzen kann, die sonst für das Warten auf E / A aufgewendet würde.

Beachten Sie, dass das oben gezeigte Threading die Effizienz nur bei stark an E / A gebundenen Prozessen steigern kann. Wenn ein Programm hauptsächlich Dinge berechnet, wird es nicht viele "Löcher" geben, in denen wir mehr arbeiten könnten. Außerdem gibt es einen Overhead von mehreren Anweisungen beim Wechseln zwischen Threads. Wenn Sie zu viele Threads ausführen, verbringt die CPU die meiste Zeit mit dem Umschalten und arbeitet nicht viel an dem Problem. Das nennt man Thrashing .

Das ist alles in Ordnung und gut für einen Single-Core-Prozessor, aber die meisten modernen Prozessoren haben zwei oder mehr Kerne. Threads dienen nach wie vor demselben Zweck - um die CPU-Auslastung zu maximieren. Diesmal können jedoch zwei separate Anweisungen gleichzeitig ausgeführt werden. Dies kann die Laufzeit um den Faktor verringern, der zur Verfügung steht, je nachdem, wie viele Kerne vorhanden sind, da der Computer tatsächlich Multitasking und nicht Kontextwechsel ausführt.

Bei mehreren Kernen bieten Threads eine Methode zum Aufteilen der Arbeit zwischen den beiden Kernen. Das oben Gesagte gilt jedoch weiterhin für jeden einzelnen Kern. Ein Programm, das eine maximale Effizienz mit zwei Threads auf einem Kern ausführt, wird höchstwahrscheinlich mit einer Spitzeneffizienz von etwa vier Threads auf zwei Kernen ausgeführt. (Die Effizienz wird hier durch minimale NOP-Befehlsausführungen gemessen.)

Die Probleme beim Ausführen von Threads auf mehreren Kernen (im Gegensatz zu einem einzelnen Kern) werden im Allgemeinen von der Hardware behoben. Die CPU stellt sicher, dass sie die entsprechenden Speicherplätze sperrt, bevor sie darauf liest / schreibt. (Ich habe gelesen, dass dafür ein spezielles Flag-Bit im Speicher verwendet wird, aber dies kann auf verschiedene Weise erreicht werden.) Als Programmierer mit höheren Sprachen müssen Sie sich keine Gedanken mehr über zwei Kerne machen müsste mit einer.

TL; DR: Threads können die Arbeit aufteilen, damit der Computer mehrere Aufgaben asynchron verarbeiten kann. Auf diese Weise kann der Computer mit maximaler Effizienz ausgeführt werden, indem die gesamte verfügbare Verarbeitungszeit genutzt wird, anstatt gesperrt zu werden, wenn ein Prozess auf eine Ressource wartet.

Michael K
quelle
17
Hervorragende Antwort. Ich wollte nur darauf eingehen, dass das Hinzufügen weiterer Threads nach einem bestimmten Zeitpunkt die Ausführung eines Programms verlangsamen kann , da die gesamte zuvor "verschwendete" Zeit genutzt wird und Sie jetzt nur noch zusätzlichen Aufwand für die Kontextumschaltung und die Synchronisierung hinzufügen.
Karl Bielefeldt
8
+1, um zu beschreiben, wie Threads bei der Verarbeitung der E / A-Blockierung helfen. Eine weitere Verwendung von Threads besteht darin, dass die Benutzeroberfläche weiterhin Benutzeraktionen (Mausbewegungen, Fortschrittsbalken, Tastenanschläge) verarbeiten kann, sodass der Benutzer davon ausgeht, dass das Programm nicht "eingefroren" ist.
JQA
3
Noch ein Zusatz: Streng genommen verleihen Threads den meisten Sprachen keine Ausdruckskraft. Sie könnten, wenn Sie unbedingt müssten, das gesamte Multiplexing selbst implementieren und die gleichen Ergebnisse erzielen. In der Praxis würde dies jedoch eine Neuimplementierung Ihrer gesamten Threading-Bibliothek bedeuten - genau wie eine Rekursion nicht unbedingt erforderlich ist, sondern durch Schreiben Ihres eigenen Aufrufstapels emuliert werden kann.
Kilian Foth
9
@james: Ich würde die UI-Verarbeitung als eine andere Form von E / A einstufen. Möglicherweise das schlechteste, da Benutzer dazu neigen, verzögert und nicht linear zu sein. :)
TMN
7
Es gibt eine stille Annahme in der Frage und Antwort, dass es sich bei dem Computer um einen einzelnen Kern oder eine CPU handelt. Meine Uhr hat zwei Kerne, ich bezweifle, dass die Annahme auf allen modernen Maschinen gültig ist.
blueberryfields
37

Was können mehrere Threads tun, was ein einzelner Thread nicht kann?

Nichts.

Einfache Beweisskizze:

  • [Church-Turing-Vermutung] ⇒ Alles, was berechnet werden kann, kann mit einer Universal-Turing-Maschine berechnet werden.
  • Eine Universal-Turing-Maschine ist einfädig.
  • Ergo kann alles, was berechnet werden kann, von einem einzelnen Thread berechnet werden.

Beachten Sie jedoch, dass sich darin eine große Annahme verbirgt: Die im einzelnen Thread verwendete Sprache ist Turing-vollständig.

So würde die interessantere Frage: „Kann das Hinzufügen nur Multi-Threading auf eine Nicht-Turing-complete Sprache macht es Turing-complete?“ Und ich glaube, die Antwort lautet "Ja".

Nehmen wir Total Functional Languages. [Für diejenigen, die nicht vertraut sind: So wie funktionale Programmierung mit Funktionen programmiert, programmiert die gesamte funktionale Programmierung mit Gesamtfunktionen.]

Total Functional Languages ​​sind offensichtlich nicht Turing-vollständig: Sie können in einer TFPL keine Endlosschleife schreiben (das ist eigentlich so ziemlich die Definition von "total"), aber Sie können in einer Turing-Maschine, dh, es gibt mindestens ein Programm, das existiert Kann nicht in eine TFPL, sondern in eine UTM geschrieben werden, daher sind TFPLs weniger rechenintensiv als UTMs.

Sobald Sie jedoch ein Threading zu einer TFPL hinzufügen, erhalten Sie Endlosschleifen: Führen Sie einfach jede Iteration der Schleife in einem neuen Thread aus. Jeder einzelne Thread gibt immer ein Ergebnis zurück, daher ist es Total, aber jeder Thread erzeugt auch einen neuen Thread, der die nächste Iteration ad infinitum ausführt .

Ich denke, dass diese Sprache Turing-vollständig sein würde.

Zumindest beantwortet es die ursprüngliche Frage:

Was können mehrere Threads tun, was ein einzelner Thread nicht kann?

Wenn Sie eine Sprache, die nicht Endlosschleifen tun können, dann Multi-Threading können Sie Endlosschleifen tun.

Beachten Sie natürlich, dass das Laichen eines Threads ein Nebeneffekt ist und daher unsere erweiterte Sprache nicht nur nicht mehr Total ist, sondern nicht einmal mehr Functional.

Jörg W. Mittag
quelle
13
Da Sie einen "Beweis" erbracht haben, kann ich mich nicht davon abhalten, Sie "tatsächlich zu ...". Ich denke, Sie missbrauchen den Begriff der Berechenbarkeit. Der Beweis ist eine nette Sache und ich verstehe, aber es ist nicht wirklich auf der richtigen Abstraktionsebene. Nicht in der Lage zu sein , Dinge zu tun, ist nicht dasselbe wie nicht zu rechnen , dh Sie können Ihren Beweis nicht verwenden, um zu sagen, dass Turing Machine "jede berechenbare Funktion" berechnen kann , es kann es in 3 Sekunden tun . Genau genommen kann eine Maschine mit einem Thread ohne Interrupts nicht auf die E / A zugreifen, während der Prozessor mit der Berechnung beschäftigt ist.
xmm0
1
Und kein echter Computer ist eine Turingmaschine.
Jens
1
@ColeJohnson: Ein Deadlock ist ein Implementierungsdetail. Die sichtbare Ausgabe ist "nicht anhalten", was mit einem einzelnen Thread problemlos möglich ist.
Heinzi
1
@Heinzi: "leicht machbar" ist eine Untertreibung. Wenn ich mich richtig erinnere, hat das zweite oder dritte Programm, das ich jemals geschrieben habe, genau das getan! ;-)
Joachim Sauer
1
Wenn das Universum Single- Thread- Ed ist, was ist dann Stringtheorie ? Mit dieser Wissenschaft kann man nicht streiten.
corsiKa
22

Theoretisch kann alles, was ein Multithread-Programm tut, auch mit einem Single-Thread-Programm ausgeführt werden, nur langsamer.

In der Praxis kann der Geschwindigkeitsunterschied so groß sein, dass für die Aufgabe kein Single-Thread-Programm verwendet werden kann. Wenn beispielsweise jede Nacht ein Stapelverarbeitungsjob ausgeführt wird und das Fertigstellen eines einzelnen Threads mehr als 24 Stunden dauert, haben Sie keine andere Wahl, als ihn multithreadingfähig zu machen. (In der Praxis liegt der Schwellenwert wahrscheinlich sogar noch darunter: Oft müssen solche Aktualisierungsaufgaben am frühen Morgen abgeschlossen sein, bevor Benutzer das System wieder verwenden können. Außerdem können andere Aufgaben davon abhängen, die ebenfalls in derselben Nacht abgeschlossen sein müssen Die verfügbare Laufzeit kann nur wenige Stunden / Minuten betragen.)

Das Ausführen von Computerarbeiten an mehreren Threads ist eine Form der verteilten Verarbeitung. Sie verteilen die Arbeit über mehrere Threads. Ein weiteres Beispiel für eine verteilte Verarbeitung (mit mehreren Computern anstelle von mehreren Threads) ist der SETI-Bildschirmschoner: Es würde schrecklich viel Zeit in Anspruch nehmen, so viele Messdaten auf einem einzelnen Prozessor zu verarbeiten, und die Forscher würden es vorziehen, die Ergebnisse vor dem Ruhestand zu sehen Sie haben nicht das Budget, um einen Supercomputer für so lange Zeit zu mieten. Deshalb verteilen sie den Auftrag auf Millionen von Haushalts-PCs, um ihn billig zu machen.

Péter Török
quelle
Der SETI-Bildschirmschoner ist ein Beispiel für das Threading in dem Sinne, dass er auf einem Hintergrund-Thread in Ihrem Computer ausgeführt wird. Sie können noch andere Aufgaben auf Ihrem Computer ausführen, während im Hintergrund Daten verarbeitet werden. Ohne den Thread würde es eine Pause geben, während der SETI-Bildschirmschoner seine Berechnungen ausführt. Sie müssten warten, bis es fertig ist, bevor Sie Ihre eigene Arbeit fortsetzen können.
Robert Harvey
3
@Robert: Ich denke, Péter verwendet SETI @ Home, um das Konzept der Aufteilung der Arbeit auf mehrere Prozessoren zu erläutern. Distributed Computing unterscheidet sich natürlich von Multithreading, aber das Konzept ist ähnlich.
Steven
5
Ich denke, das SETI-Beispiel ist eher verteiltes Rechnen als Multithread. Ähnliche Konzepte, aber nicht dasselbe.
11

Zwar scheinen Threads nur einen kleinen Schritt von der sequentiellen Berechnung entfernt zu sein, sie stellen jedoch einen großen Schritt dar. Sie verwerfen die wichtigsten und attraktivsten Eigenschaften der sequentiellen Berechnung: Verständlichkeit, Vorhersagbarkeit und Determinismus. Threads sind als Berechnungsmodell absolut nicht deterministisch, und die Aufgabe des Programmierers besteht darin, diesen Nichtdeterminismus zu beschneiden.

- Das Problem mit Threads (www.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-1.pdf).

Zwar bietet die Verwendung von Threads einige Leistungsvorteile, da Sie die Arbeit auf mehrere Kerne verteilen können, sie sind jedoch häufig mit einem hohen Preis verbunden.

Einer der Nachteile der Verwendung von Threads, die hier noch nicht erwähnt wurden, ist der Verlust der Ressourcenkompartimentierung, den Sie mit einzelnen Thread-Prozessbereichen erhalten. Angenommen, Sie stoßen auf einen Segfault. In einigen Fällen ist es möglich, dies in einer Multi-Prozess-Anwendung zu beheben, indem Sie das fehlerhafte Kind einfach sterben lassen und ein neues Kind neu erschaffen. Dies ist im Prefork-Backend von Apache der Fall. Wenn eine httpd-Instanz ausfällt, ist der schlimmste Fall, dass die bestimmte HTTP-Anforderung für diesen Prozess gelöscht wird, Apache jedoch ein neues untergeordnetes Element erzeugt und die Anforderung häufig nur erneut gesendet und bearbeitet wird. Das Endergebnis ist, dass Apache als Ganzes nicht mit dem fehlerhaften Thread beendet wird.

Eine weitere Überlegung in diesem Szenario sind Speicherverluste. Es gibt einige Fälle, in denen Sie einen Thread-Absturz problemlos handhaben können (unter UNIX ist die Wiederherstellung nach bestimmten Signalen - auch bei einem Fehler / einer Verletzung - möglich), aber selbst in diesem Fall haben Sie möglicherweise den gesamten von diesem Thread zugewiesenen Speicher verloren (Malloc, neu, etc.). Während Sie also möglicherweise weiterarbeiten, geht mit jedem Fehler / jeder Wiederherstellung im Laufe der Zeit immer mehr Speicher verloren. Auch hier gibt es einige Möglichkeiten, dies zu minimieren, beispielsweise die Verwendung von Speicherpools durch Apache. Dies schützt jedoch nicht vor Speicher, der möglicherweise von Drittanbieter-Bibliotheken zugewiesen wurde, die der Thread möglicherweise verwendet hat.

Und wie einige Leute bereits betont haben, ist es vielleicht am schwierigsten, Synchronisationsprimitive richtig zu verstehen. Dieses Problem selbst - nur die allgemeine Logik für all Ihren Code richtig zu machen - kann große Kopfschmerzen bereiten. Mysteriöse Deadlocks können in den seltsamsten Zeiten auftreten, und manchmal sogar erst, wenn Ihr Programm in der Produktion ausgeführt wird, was das Debuggen umso schwieriger macht. Hinzu kommt die Tatsache, dass Synchronisationsprimitive je nach Plattform (Windows vs. POSIX) häufig stark variieren und das Debuggen oft schwieriger ist, sowie die Möglichkeit, dass die Race-Bedingungen jederzeit gegeben sind (Start / Initialisierung, Laufzeit und Herunterfahren). Das Programmieren mit Threads hat für Anfänger wirklich wenig Gnade. Und auch für Experten, es gibt immer noch wenig Gnade, nur weil das Wissen über das Threading selbst die Komplexität im Allgemeinen nicht minimiert. Manchmal scheint jede Zeile von Thread-Code die Gesamtkomplexität des Programms exponentiell zu verschärfen und die Wahrscheinlichkeit zu erhöhen, dass ein versteckter Deadlock oder eine seltsame Race-Bedingung zu irgendeinem Zeitpunkt auftritt. Es kann auch sehr schwierig sein, Testfälle zu schreiben, um diese Dinge herauszufiltern.

Aus diesem Grund basieren einige Projekte wie Apache und PostgreSQL größtenteils auf Prozessen. PostgreSQL führt jeden Backend-Thread in einem separaten Prozess aus. Dies lindert natürlich immer noch nicht das Problem der Synchronisation und der Rennbedingungen, aber es bietet einiges an Schutz und vereinfacht in gewisser Weise die Dinge.

Mehrere Prozesse, die jeweils einen einzelnen Ausführungsthread ausführen, können viel besser sein als mehrere Threads, die in einem einzelnen Prozess ausgeführt werden. Mit dem Aufkommen eines Großteils des neuen Peer-to-Peer-Codes wie AMQP (RabbitMQ, Qpid usw.) und ZeroMQ ist es viel einfacher, Threads auf verschiedene Prozessbereiche und sogar auf Maschinen und Netzwerke aufzuteilen, was die Dinge erheblich vereinfacht. Aber es ist keine Wunderwaffe. Es bleibt immer noch Komplexität zu bewältigen. Sie verschieben nur einige Ihrer Variablen aus dem Prozessbereich in das Netzwerk.

Das Fazit ist, dass die Entscheidung, in die Domäne der Threads einzusteigen, keine leichte ist. Sobald Sie dieses Territorium betreten, wird fast augenblicklich alles komplexer und es treten ganz neue Arten von Problemen in Ihrem Leben auf. Es kann lustig und cool sein, aber es ist wie mit Atomkraft - wenn etwas schief geht, kann es schlecht und schnell gehen. Ich erinnere mich, dass ich vor vielen Jahren einen Kurs in Kritikalitätstraining besucht habe und sie zeigten Bilder von Wissenschaftlern in Los Alamos, die im Zweiten Weltkrieg in den Labors mit Plutonium spielten. Viele haben wenig oder gar keine Vorsichtsmaßnahmen gegen den Fall einer Exposition getroffen, und im Handumdrehen - mit einem einzigen hellen, schmerzlosen Blitz wäre alles für sie vorbei. Tage später waren sie tot. Richard Feynman bezeichnete dies später als " Kitzeln des Drachenschwanzes""So kann es sein, mit Fäden zu spielen (zumindest für mich). Zuerst scheint es ziemlich harmlos zu sein, und als du gebissen wirst, kratzst du dir am Kopf, wie schnell die Dinge sauer werden. Aber zumindest haben die Fäden gewonnen töte dich nicht

Mike Owens
quelle
Meine TL; DR-Antwort verblasst im Vergleich zu diesem schönen Stück darüber, was einzelne Threads nicht für Sie tun.
bmike
1
Kaffeepause am Nachmittag
Mike Owens
10

Erstens wird eine Single-Threaded-Anwendung niemals die Vorteile einer Multi-Core-CPU oder eines Hyper-Threading nutzen. Aber selbst auf einem einzelnen Kern hat eine Single-Threaded-CPU, die Multi-Threading ausführt, Vorteile.

Überlegen Sie sich die Alternative und ob Sie das glücklich macht. Angenommen, Sie haben mehrere Aufgaben, die gleichzeitig ausgeführt werden müssen. Zum Beispiel müssen Sie mit zwei verschiedenen Systemen kommunizieren. Wie geht das ohne Multithreading? Sie würden wahrscheinlich Ihren eigenen Scheduler erstellen und die verschiedenen Aufgaben aufrufen lassen, die ausgeführt werden müssen. Dies bedeutet, dass Sie Ihre Aufgaben in Teile aufteilen müssen. Wahrscheinlich müssen Sie einige Echtzeitbeschränkungen erfüllen, um sicherzustellen, dass Ihre Teile nicht zu viel Zeit in Anspruch nehmen. Andernfalls läuft der Timer bei anderen Aufgaben ab. Dies erschwert die Aufteilung einer Aufgabe. Je mehr Aufgaben Sie selbst verwalten müssen, desto mehr Aufteilung ist erforderlich und desto komplexer wird Ihr Scheduler, um alle Einschränkungen zu erfüllen.

Wenn Sie mehrere Threads haben, kann das Leben einfacher werden. Ein präventiver Scheduler kann einen Thread jederzeit stoppen, seinen Status beibehalten und einen anderen Thread neu starten. Es wird neu gestartet, wenn Ihr Thread an der Reihe ist. Vorteile: Die Komplexität, einen Scheduler zu schreiben, wurde bereits für Sie erledigt und Sie müssen Ihre Aufgaben nicht aufteilen. Der Scheduler ist auch in der Lage, Prozesse / Threads zu verwalten, die Sie selbst nicht kennen. Und wenn ein Thread nichts tun muss (auf ein Ereignis wartet), nimmt er auch keine CPU-Zyklen in Anspruch. Dies ist nicht so einfach, wenn Sie Ihren Down-Single-Threaded-Scheduler erstellen. (Etwas einzuschlafen ist nicht so schwierig, aber wie wacht es auf?)

Der Nachteil der Multithread-Entwicklung besteht darin, dass Sie sich mit Parallelitätsproblemen, Sperrstrategien usw. auskennen müssen. Das Entwickeln von fehlerfreiem Multithread-Code kann sehr schwierig sein. Und das Debuggen kann noch schwieriger sein.

Kennzeichen
quelle
9

Gibt es etwas, das nur durch die Verwendung mehrerer Threads erreicht werden kann?

Ja. Sie können keinen Code auf mehreren CPUs oder CPU-Kernen mit einem einzigen Thread ausführen.

Ohne mehrere CPUs / Kerne können Threads weiterhin parallel laufenden Code vereinfachen, z. B. die Client-Verarbeitung auf einem Server. Sie können jedoch dasselbe auch ohne Threads tun.

Schlamm
quelle
6

Bei Threads geht es nicht nur um Geschwindigkeit, sondern auch um Parallelität.

Wenn Sie keine Batch-Anwendung wie @Peter vorgeschlagen haben, sondern stattdessen ein GUI-Toolkit wie WPF, wie Sie mit nur einem Thread mit Benutzern und Geschäftslogik interagieren könnten?

Angenommen, Sie erstellen einen Webserver. Wie würden Sie mehr als einen Benutzer gleichzeitig mit nur einem Thread bedienen (vorausgesetzt, keine anderen Prozesse)?

Es gibt viele Szenarien, in denen ein einfacher Thread nicht ausreicht. Aus diesem Grund finden jüngste Fortschritte wie der Intel MIC-Prozessor mit mehr als 50 Kernen und Hunderten von Threads statt.

Ja, parallele und gleichzeitige Programmierung ist schwierig. Aber notwendig.

Randolf Rincón Fadul
quelle
3
"Nur ein Thread" kann mehrere Bedeutungen haben - Sie können begrenzte Fortsetzungen verwenden, um beispielsweise kooperatives Multitasking innerhalb eines einzelnen (Betriebssystem- oder grünen) Threads zu simulieren.
Frank Shearar
Klar, aber ich gebe der Frage @Frank +1 für den Tipp einen kleinen Kontext.
Randolf Rincón Fadul
3
Threads mögen es einfacher machen, aber alle Aufgaben, die Sie erwähnt haben, können ohne Threads erledigt werden und waren es auch. Beispielsweise war die kanonische GUI-Schleife while (true) {GUI-Ereignisse verarbeiten; rechne ein bisschen}. Keine Threads benötigt.
KeithB
Es gibt Single-Process-Single-Thread-Webserver. lighttpd zum Beispiel oder thttpd oder nginx (obwohl letzterer mehr als einen Prozess ausführen kann, aber der Standard ist einer. Es ist auch immer ein Singlethread). Sie haben absolut kein Problem damit, mehr als einen Benutzer zu bedienen.
StasM
6

Durch Multi-Threading kann die GUI-Oberfläche bei langen Verarbeitungsvorgängen immer noch reagieren. Ohne Multithreading kann der Benutzer ein gesperrtes Formular nicht mehr beobachten, während ein langer Prozess ausgeführt wird.

LarsTech
quelle
Das ist nicht ganz richtig. Theoretisch könnten Sie Ihren langen Vorgang in viele kleinere aufteilen und die Ereignisverarbeitung dazwischen aktualisieren.
Sebastian Negraszus
@Sebastion N. Aber wenn der lange Prozess nicht in kleinere umgestaltet werden kann? Die Möglichkeit, den Prozess in einem anderen Thread auszuführen, kann dazu führen, dass der GUI-Thread (der Haupt-Thread) nicht mehr reagiert.
LarsTech
5

Multithread-Code kann die Programmlogik blockieren und auf veraltete Daten zugreifen, wie dies mit einzelnen Threads nicht möglich ist.

Threads können einen obskuren Fehler von etwas übernehmen, von dem ein durchschnittlicher Programmierer erwartet, dass es ihn debuggt, und ihn in den Bereich verschieben, in dem Geschichten über das Glück erzählt werden, das erforderlich ist, um den gleichen Fehler mit heruntergefahrenen Hosen zu fangen, als ein alarmierender Programmierer zufällig nur den Fehler ansah richtiger Moment.

bmike
quelle
4

Apps, die sich mit dem Blockieren von E / A befassen und auch auf andere Eingaben (die grafische Benutzeroberfläche oder andere Verbindungen) reagieren müssen, können nicht als Singlethread ausgeführt werden

Die Hinzufügung von Überprüfungsmethoden in der IO-Bibliothek, um festzustellen, wie viel gelesen werden kann, ohne zu blockieren, kann dazu beitragen, dass jedoch nicht viele Bibliotheken vollständige Garantien dafür abgeben

Ratschenfreak
quelle
Sie können einfädig sein (und waren es oft). Der Code weist den Kernel an, einige E / A-Vorgänge zu starten, und erhält ein Signal, wenn der Vorgang abgeschlossen ist. Während auf das Signal gewartet wird, kann es eine andere Verarbeitung durchführen.
KeithB
@keith das ist nicht- blockierend IO Ich sagte ausdrücklich Blockieren
Ratschenfreak
Aber warum sollten Sie E / A blockieren, wenn Sie weiterhin reagieren möchten? Gibt es blockierende E / A, für die es im Allgemeinen keine nicht blockierende Alternative gibt? Mir fällt nichts ein.
KeithB
@keith Java RMI blockiert und während Sie einen Rückruf implementieren können (aber das schafft einen anderen Thread) mit ihm, aber das kann von Firewalls blockiert werden
Ratschenfreak
Das klingt nach einer Einschränkung von Java RMI, nicht nach einer inhärenten Einschränkung von Single-Threaded-Code. Außerdem, wenn Sie RMI ausführen, haben Sie einen separaten "Thread" der Ausführung, nur auf einer entfernten Maschine.
KeithB
4

Viele gute Antworten, aber ich bin mir nicht sicher, ob es so aussieht wie ich - Vielleicht bietet dies eine andere Sichtweise:

Threads sind nur eine Vereinfachung der Programmierung wie Objekte oder Akteure oder für Schleifen (Ja, alles, was Sie mit Schleifen implementieren, können Sie mit if / goto implementieren).

Ohne Threads implementieren Sie einfach eine State Engine. Ich musste das oft tun (das erste Mal, als ich es tat, hatte ich noch nie davon gehört - ich habe nur eine große switch-Anweisung gemacht, die von einer "State" -Variable gesteuert wird). Zustandsautomaten sind immer noch weit verbreitet, können aber ärgerlich sein. Mit Fäden verschwindet ein riesiger Brocken der Kesselplatte.

Sie machen es einer Sprache auch einfacher, ihre Laufzeitausführung in Multi-CPU-freundliche Blöcke zu unterteilen (so wie Actors, glaube ich).

Java bietet "grüne" Threads auf Systemen, auf denen das Betriebssystem KEINE Thread-Unterstützung bietet. In diesem Fall ist es einfacher zu erkennen, dass es sich eindeutig nur um eine Programmierabstraktion handelt.

Bill K
quelle
+1 - Threads bieten nur eine Abstraktion für Parallelität auf im Grunde sequentiellen Maschinen, genau wie Programmiersprachen auf hoher Ebene eine Abstraktion für Maschinencode bereitstellen.
Mouviciel
0

Betriebssysteme verwenden ein Time-Slicing-Konzept, bei dem jeder Thread seine Ausführungszeit erhält und dann vorbelegt wird. Ein solcher Ansatz kann das derzeitige Threading ersetzen, aber das Schreiben von eigenen Schedulern in jeder Anwendung wäre übertrieben. Außerdem müssten Sie mit E / A-Geräten usw. arbeiten. Und würde eine gewisse Unterstützung von der Hardwareseite erfordern, damit Sie Interrupts auslösen können, damit Ihr Scheduler ausgeführt wird. Grundsätzlich würden Sie jedes Mal ein neues Betriebssystem schreiben.

Im Allgemeinen kann Threading die Leistung in Fällen verbessern, in denen Threads auf E / A warten oder inaktiv sind. Außerdem können Sie reaktionsschnelle Schnittstellen erstellen und Prozesse stoppen, während Sie lange Aufgaben ausführen. Threading verbessert außerdem die Leistung echter Multicore-CPUs.

Coder
quelle
0

Erstens können Threads zwei oder mehr Dinge gleichzeitig erledigen (wenn Sie mehr als einen Kern haben). Sie können dies zwar auch mit mehreren Prozessen durchführen, einige Aufgaben verteilen sich jedoch nicht sehr gut auf mehrere Prozesse.

Außerdem enthalten einige Aufgaben Leerzeichen, die Sie nicht so einfach umgehen können. Zum Beispiel ist es schwierig, Daten aus einer Datei auf der Festplatte zu lesen und gleichzeitig von Ihrem Prozess etwas anderes ausführen zu lassen. Wenn für Ihre Aufgabe unbedingt viele Daten von der Festplatte gelesen werden müssen, verbringt Ihr Prozess viel Zeit damit, auf die Festplatte zu warten, unabhängig davon, was Sie tun.

Zweitens können Threads es Ihnen ermöglichen, zu vermeiden, große Mengen Ihres Codes optimieren zu müssen, die nicht leistungskritisch sind. Wenn Sie nur einen Thread haben, ist jeder Code leistungskritisch. Wenn es blockiert, sind Sie gesunken - keine Aufgaben, die von diesem Prozess erledigt würden, können Fortschritte machen. Bei Threads wirkt sich ein Block nur darauf aus, dass der Thread und andere Threads mitkommen und Aufgaben bearbeiten können, die von diesem Prozess ausgeführt werden müssen.

Ein gutes Beispiel ist selten ausgeführter Fehlerbehandlungscode. Angenommen, bei einer Aufgabe ist ein sehr seltener Fehler aufgetreten, und der Code zur Behandlung dieses Fehlers muss in den Speicher geschrieben werden. Wenn der Datenträger ausgelastet ist und der Prozess nur einen einzelnen Thread enthält, kann kein Vorwärtsfortschritt ausgeführt werden, bis der Code zur Behandlung dieses Fehlers in den Speicher geladen werden kann. Dies kann zu einer stoßweisen Reaktion führen.

Ein anderes Beispiel ist, wenn Sie sehr selten eine Datenbanksuche durchführen müssen. Wenn Sie auf die Antwort der Datenbank warten, wird Ihr Code eine große Verzögerung erfahren. Sie möchten sich jedoch nicht die Mühe machen, den gesamten Code asynchron zu machen, da dies so selten vorkommt, dass Sie diese Suchvorgänge durchführen müssen. Mit einem Thread für diese Arbeit erhalten Sie das Beste aus beiden Welten. Ein Thread für diese Arbeit macht es nicht leistungskritisch, wie es sein sollte.

David Schwartz
quelle