In der Informatik bezeichnet Thread [] (, ‚Faden‘, ‚Strang‘) – auch Aktivitätsträger oder leichtgewichtiger Prozess genannt – einen Ausführungsstrang oder eine Ausführungsreihenfolge in der Abarbeitung eines Programms. Ein Thread ist Teil eines Prozesses.
Es wird zwischen zwei Arten von Threads unterschieden: Threads im engeren Sinne, die sogenannten Kernel-Threads, laufen ab unter Steuerung durch das Betriebssystem. Im Gegensatz dazu stehen die User-Threads, die das Computerprogramm des Anwenders komplett selbst verwalten muss.
Dieser Artikel hier behandelt den Thread im engeren Sinne, also den Kernelthread.
Threads aus Sicht des Betriebssystems mini|Mehrere Kernelthreads in einem Prozess (Task) Ein (Kernel-)Thread ist ein sequentieller Abarbeitungslauf innerhalb eines Prozesses und teilt sich mit den anderen vorhandenen Threads (Multithreading) des zugehörigen Prozesses eine Reihe von Betriebsmitteln: das Codesegment, das Datensegment, die verwendeten Dateideskriptoren (ggf. im Datensegment). Historisch wurde dafür von Friedrich L. Bauer der Begriff Sequentieller Prozess geprägt.
Threads innerhalb des gleichen Prozesses können verschiedenen Prozessoren zugeordnet sein. Jeder Thread besitzt einen eigenen sogenannten Threadkontext: unabhängiger Registersatz inkl. Befehlszeiger (Instruction Pointer), einen eigenen Stapel (Stack), jedoch meist im gemeinsamen Prozess-Adressraum. Als Besonderheit kann es Betriebsmittel geben, die nur von dem erzeugenden Thread benutzt werden können oder dürfen (Beispiel: Thread-local storage, Window-Handle). Andere Betriebsmittel werden von allen Threads gemeinsam verwendet. Durch die gemeinsame Nutzung von Betriebsmitteln kann es auch zu Konflikten kommen. Diese müssen durch den Einsatz von Synchronisationsmechanismen aufgelöst werden.
Da Threads, die demselben Prozess zugeordnet sind, den gleichen Adressraum verwenden, ist eine Kommunikation zwischen diesen Threads von vornherein sehr einfach möglich (vgl. mit Interprozesskommunikation bei Prozessen).
Jeder „Programmfaden“ ist für die Ausführung einer bestimmten Aufgabe verantwortlich. Die Ausführungsstränge der Programmfunktionen können damit in überschaubare Einheiten aufgeteilt werden sowie umfangreiche Aufgaben auf mehrere Prozessorkerne verteilt werden.
Bei den meisten Betriebssystemen kann ein Thread neben den Zuständen aktiv (englisch running), bereit (englisch ready) und blockiert (englisch waiting) auch einen Zustand inaktiv annehmen. Im Zustand ‚rechnend‘ (=aktiv =running) findet die Ausführung von Befehlen auf der CPU statt, bei ‚rechenbereit‘ (=bereit =ready) ist der Thread gestoppt, um einen anderen Thread rechnen zu lassen und bei ‚blockiert‘ (=waiting) wartet der Thread auf ein Ereignis (meist dass ein Betriebssystem-Dienst fertig/durchgeführt wurde). Ein Thread im Zustand ‚inaktiv‘ wird meist gerade vom Betriebssystem neu eingerichtet oder hat fertiggerechnet und kann vom Betriebssystem nun aus der Threadliste entfernt oder anderweitig wiederverwendet werden.
Bedeutungsunterschied (Kernel-)Thread gegenüber Prozess, Task und User Thread Ein Prozess bezeichnet den Ablauf eines Computerprogrammes auf einem oder mehreren Prozessor(en). Einem Prozess sind ein Adressraum und weitere Betriebssystemmittel zugeordnet – insbesondere sind Prozesse gegeneinander abgeschirmt: Versucht ein Prozess, auf Adressen oder Betriebsmittel zuzugreifen, die ihm nicht zugeteilt wurden (und ggf. einem anderen Prozess gehören), so schlägt dies fehl und er wird vom Betriebssystem abgebrochen. Ein Prozess kann mehrere Threads oder – wenn bei dem Programmablauf keine Parallelverarbeitung vorgesehen ist – auch nur einen einzigen Thread beinhalten. Threads teilen sich innerhalb eines Prozesses Prozessoren, den Speicher und andere betriebssystemabhängige Ressourcen wie Dateien und Netzwerkverbindungen. Deswegen ist der Verwaltungsaufwand für Threads üblicherweise geringer als der für Prozesse. Ein wesentlicher Effizienzvorteil von Threads besteht zum einen darin, dass im Gegensatz zu Prozessen beim Threadwechsel kein vollständiger Wechsel des Prozesskontextes notwendig ist, da alle Threads einen gemeinsamen Teil des Prozesskontextes verwenden, zum anderen in der einfachen Kommunikation und schnellem Datenaustausch zwischen Threads.
Bereits in den 1980er Jahren gab es sogenannte Multitask-Betriebssysteme, da im Unterschied zu den joborientierten Systemen insbesondere in der damals so bezeichneten Prozessrechentechnik mehrere Aufgaben quasi parallel ausgeführt werden mussten. Damals hat sich der Begriff Task für eine Aufgabe aus Sicht des Betriebssystems eingebürgert, das ist ein Synonym zu Prozess. Der Begriff Task (deutsch: Aufgabe) wird in der Softwarearchitektur aber auch unscharf allgemein für zusammenhängende Aufgaben benutzt, und insbesondere auch selten synonym für Thread eingesetzt.
Ein Thread ist wörtlich ein einzelner Ausführungsfaden eines Programms, der Begriff Thread wird aber auf den Ausführungsstrang aus Sicht des Betriebssystems eingesetzt (Kernelthread). In einer Anwendersoftware kann dieser Ausführungsstrang durch geeignete Programmierung nochmals in unabhängige Einzelstränge unterteilt werden. Im englischen Sprachraum hat sich für einen einzelnen solchen Ausführungsstrang der Anwendersoftware der Begriff User-Thread (bei Microsoft Fiber, deutsch: Faser) eingebürgert. Bei User Threads ist allein die Anwendersoftware für die Verwaltung ihrer Ausführungsstränge zuständig.
Beispiele Die folgende Tabelle zeigt Beispiele für die verschiedenen Kombinationen aus Prozess, Kernel- und User-Thread:
Prozess Kernel-Thread User-Thread Beispiel Nein Nein Nein Ein Computerprogramm unter MS-DOS ablaufend. Das Programm kann zu einer Zeit nur eine von drei Aktionen ausführen. Nein Nein Ja Windows 3.1 auf der Oberfläche von DOS. Alle Windows-Programme laufen in einem einfachen Prozess, ein Programm kann den Speicher eines anderen Programms zerstören, was jedoch bemerkt wird und einen General Protection Fault (Allgemeine Schutzverletzung) zur Folge hat. Nein Ja Nein Ursprüngliche Implementierung des Amiga OS. Das Betriebssystem unterstützt vollständig Threads und erlaubt dabei, dass mehrere Applikationen unabhängig voneinander ablaufen, vom Betriebssystemkern zeitlich geplant. Wegen der nicht vorhandenen Prozessunterstützung ist das System effizienter (wegen der Vermeidung des Zusatzaufwandes des Speicherschutzes), mit dem Preis, dass Applikationsfehler den gesamten Computer lahmlegen können. Nein Ja Ja DR-DOS 7.01, 7.03, 8.0; Enhanced DR-DOS alle Versionen Mac OS 9 unterstützt User-Threads mittels Apples Thread Manager und Kernelthreads mittels Apples Multiprocessing Services, das mit dem nanokernel arbeitet, eingeführt in Mac OS 8.6. Damit werden Threads unterstützt, aber immer noch das Verfahren des MultiFinder zum Verwalten von Applikationen benutzt. Ja Nein Nein Die meisten bekannten Implementierungen von Unix (außer Linux). Das Betriebssystem kann mehr als ein Programm zu einem Zeitpunkt ausführen, die Programmabarbeitungen sind gegeneinander geschützt. Wenn ein Programm sich falsch verhält, kann es seinen eigenen Prozess stören, was das Beenden dieses einen Prozesses zur Folge haben kann, ohne dass das Betriebssystem oder andere Prozesse gestört werden. Jedoch kann der Informationsaustausch zwischen Prozessen entweder fehlerträchtig sein (bei Nutzungen von Techniken wie shared Memory) oder aufwendig (bei Nutzung von Techniken wie message passing). Die asynchrone Ausführung von Aufgaben benötigt einen aufwendigen fork() Systemaufruf. Ja Nein Ja Sun OS (Solaris) von Sun. Sun OS ist Sun Microsystems Version von Unix. Sun OS implementiert User-Threads als sogenannte green threads um einem einfachen Prozess die asynchrone Ausführung mehrerer Aufgaben zu ermöglichen, beispielsweise playing a sound, repainting a window, oder auf ein Bedienerereignis zu reagieren wie die Anwahl des stop button. Obwohl Prozesse präemptiv verwaltet werden, arbeiten die green threads kooperativ. Dieses Modell wird oft an Stelle von richtigen Threads genutzt und ist in Mikrocontrollern und sogenannten embedded devices immer noch aktuell, und wird sehr häufig genutzt.
Windows 3.x im Enhanced Mode bei Verwendung von DOS-Boxen fällt ebenfalls in diese Kategorie, da die DOS-Boxen eigenständige Prozesse mit abgeschottetem Adressraum darstellen. Ja Ja Nein Dies ist der allgemeine Fall der Applikationen unter Windows NT ab 3.51 SP3+, Windows 2000, Windows XP, Mac OS X, Linux, und anderen modernen Betriebssystemen. Alle diese Betriebssysteme erlauben dem Programmierer die Nutzung von User-Threads beziehungsweise von Bibliotheken, die selbst User-Threads verwenden, jedoch nutzen nicht alle Programme diese Möglichkeit aus. Weiterhin können User-Threads auch vom Betriebssystem automatisch für jede gestartete Applikation angelegt werden (zum Beispiel zur Bedienung der grafischen Benutzeroberfläche), ohne dass dies explizit vom Programmierer getan werden muss; solche Programme sind dann automatisch multithreaded. Außerdem ist es notwendig, mehrere User-Threads zu verwenden, wenn man mehrere Prozessoren/Prozessorkerne in der Anwendung ausnutzen möchte. Ja Ja Ja Die meisten Betriebssysteme seit 1995 fallen in diese Kategorie. Die Nutzung von Threads zur gleichzeitigen Ausführung ist die gewöhnliche Auswahl, obwohl es auch multi-process- und multi-fiber-Applikationen gibt. Diese werden beispielsweise benutzt, damit ein Programm seine grafische Benutzerschnittstelle abarbeiten kann, während es gleichzeitig auf eine Eingabe des Benutzers wartet oder andere Arbeiten im Hintergrund ausführt. Bemerkungen: Die Nutzung von User-Threads ist prinzipiell unabhängig vom Betriebssystem. Sie ist damit mit jedem Betriebssystem möglich. Wichtig ist nur, dass sich der komplette Zustand des Prozessors auslesen und wieder zurückschreiben lässt (User-Threads sind auch in einigen 8-Bit-Betriebssystemen implementiert worden, so z. B. als GEOS auf dem C64/C128). Daher sind die Werte in der Tabelle als Anhaltswerte zu sehen. Einige moderne Betriebssysteme (z. B. Linux) lassen keine strikte Unterscheidung zwischen Prozessen und Kernel-Threads mehr zu. Beides geschieht mit demselben Systemruf (clone(2)), man kann feingranular angeben, welche Ressourcen geteilt und welche nicht geteilt werden (Ausnahme: CPU-Register, Stack). Bei etlichen Ressourcen kann man das zur Laufzeit des Threads sogar noch ändern (Speicher: TLS vs. Shared Memory, File Handle: socketpair).
Implementierungen Java In Java ist ein Arbeiten mit mehreren Threads von vornherein vorgesehen. Dabei funktioniert das Multithreading auch, wenn das Betriebssystem dieses nicht oder nur mangelhaft unterstützt. Möglich ist das, weil die virtuelle Maschine von Java die Threadumschaltung einschließlich Stackverwaltung übernehmen kann. In Betriebssystemen mit Threadunterstützung können die Betriebssystemeigenschaften direkt genutzt werden. Die Entscheidung darüber liegt in der Programmierung der virtuellen Maschine.
In Java gibt es im Basis-Package java.lang die Klasse Thread. Instanzen dieser Klasse sind Verwaltungseinheiten der Threads. Thread kann entweder als Basisklasse für eine Anwenderklasse benutzt werden, oder eine Instanz von Thread kennt eine Instanz einer beliebigen Anwenderklasse. Im zweiten Fall muss die Anwenderklasse die Schnittstelle java.lang.Runnable implementieren und demzufolge eine Methode run() enthalten.
Ein Thread wird gestartet mittels Aufruf von thread.start(). Dabei wird die zugeordnete run()-Methode abgearbeitet. Solange run() läuft, ist der Thread aktiv.