=encoding utf-8 =head1 Threads in dynamischen Sprachen und warum Perl-Pseudo-Threads sterben sollten Wie man am Titel sieht, soll es im folgenden um zwei verwandte Themen gehen. Einerseits möchte ich grundsätzliche Gedanken über Threads liefern, einige Vorurteile ausräumen und erläutern, wie Threads in dynamischen Sprachen implementiert werden und warum dies so ist. Andererseits möchte ich erläutern, warum Perl keine Threads unterstützt und weshalb Perls sogenannte Threads eher sterben sollten, da sie mehr Schaden anrichten als sie helfen. Letzteres muss nicht einmal zu Inkompatibilitäten führen: da Perl-Threads keine sind, lassen sie sich effizient ohne emulieren. =head2 Was ist ein Thread - Eine Begriffsbestimmung Am einfachsten erklärt man Threads, in dem man sie Prozessen gegenüberstellt: Prozesse haben (üblicherweise) einen eigenen Adressraum, d.h. Variablen existieren als Kopie in jedem Prozess und sind unabhängig voneinander. Außerdem besitzt ein Prozess eine "Continuation", d.h. im wesentlichen den aktuellen "Ort" der Programmausführung. Unter Unix kommen dann noch andere Ressourcen wie File-Descriptoren hinzu, aber im wesentlichen ist es das. Ein Thread nun ist im wesentlichen ein abgespeckter Prozess (daher werden sie auch oft "LWPs" - Light Weight Processes genannt), bei dem eine oder mehrere dieser Ressourcen gemeinsam genutzt werden, insbesondere der Adressraum. Daher kann man Threads auch als Untereinheit von Prozessen ansehen: ein Prozess kann aus ein oder mehreren Threads bestehen, die sich alle den gleichen Adressraum (die gleichen Variableninhalte) teilen. Bei Threads gibt eine Menge verschiedene Variationen. Die wichtigsten sind: =over 4 =item Kooperative Threads Die "Urform" der Threads - diese Threads werden "kooperativ" genannt, nicht, weil sie es sind, sondern, weil sie es sein müssen: Sobald ein Thread ausgeführt wird, läuft dieser ohne Unterbrechung, bis er freiwillig die CPU an einen anderen Thread abgibt. Tut ein Thread dies nicht, läuft nichts anderes im System. Manchmal werden Kooperative Threads auch Koroutinen, Fibers oder Continuations genannt, was genau genommen falsch ist, aber im wesentlichen bezeichnen diese Begriffe dasselbe. Windows 3.11, das Oberon-System oder das Coro-Modul von Perl sind Beispiele für kooperatives Threading. =item Preemptive Threads Um das Problem der Monopolisierung des Rechners durch einen unkooperativen (weil z.B. ferhlerhaften) Thread zu umgehen, wurden "preemptive" Threads erfunden: Diese werden ohne ihr Zutun regelmäßig "unterbrochen" (z.B. durch einen Timer) und dadurch von der CPU "verdrängt" (preempted). Diese Art von Threads wird oft auch "time-slicing" (Zeitscheibenverfahren) genannt, da hierbei ein Thread üblicherweise eine bestimmte Zahl von Zeiteinheiten erhält und, wenn er in dieser Zeit die CPU nicht freigibt, wird er automatisch verdrängt. Neuere Windows-Versionen, alle Unix-Versionen und insbesondere alle Thread-Systeme dynamischer Sprachen (d.h. Ruby, Python usf.) benutzen dieses Verfahren. =item "Kernel-Threads" und "Userspace-Threads" Ein weiterer wichtiger Unterschied zwischen Thread-Systemen ist, ob sie vom Betriebssystem selbst implementiert werden oder innerhalb eines Prozesses. In letzterem Fall teilen sich alle Threads dieselbe Prozess-Continuation, was nichts anderes bedeutet, als daß diese Threads niemals parallel laufen können (z.B. auf einem SMP-System). Der Begriff "Kernel-Thread" bezeichnet im Gegensatz dazu die Variante, bei er die Threads tatsächlich parallel/gleichzeitig arbeiten können, obwohl dies nicht zwangsweise im "Kernel" implementiert werden muss. Userspace-Threads sind im allgemeinen um ein vielfaches schneller als Kernel-Threads - die Userspace-Threading-Bibliothek, die von Coro benutzt wird schaltet mehr als 100 mal schneller zwischen Threads um als der Linux-Kernel. Selbst auf Perl-Ebene schalten die Userspace-Threads von Coro mehr als 14 mal schneller als die Linux-Kernel-Threads. =back =head2 Wozu wurden Threads erfunden, wozu eignen sie sich? =head3 Single-Cores Multi-Core und Multiprozessorsysteme sind erst seit kurzer Zeit verbreitet. Die überwiegende Mehrheit der Rechner besitzt nach wie vor nur einzelne CPUs. Moderne CPUs besitzen im allgemeinen spezielle Hardware, um Prozesse zu unterstützen, z.B. eine Memory-Management-Unit (MMU) um den Adressraum zu verwalten und einen Translation-Lookaside-Buffer (TLB) um das ganze zu beschleunigen. Da jeder Prozess seinen eigenen Adressraum besitzt, muss die MMU bei jedem Umschalten neu konfiguriert werden, Caches wie der TLB gehen häufig verloren. Dies kann die Performance empfindlich beeinflussen, da die meisten CPUs nur einen Zustand im Cache behalten können. Die natürliche Lösung gegen zu viele Rekonfigurationen sind weniger Rekonfigurationen bei Prozesswechseln. Wechselt man den Adressraum nicht, so erhält man Threads auf natürliche Weise, da die CPU einfach den alten Adressraum weiter benutzt. Daher stammt der Name "Light Weight Processes" (leichtgewichtige Prozesse): Threads werden hier als Prozesse mit weniger "Zustand" behandelt. Dies beschleunigt das Prozessumschalten erheblich, vor allem auf älteren Systemen. =head3 Multiprozessor- oder Multi-Core-Systeme Ein weit verbreiteter Irrtum ist es, daß Threads sich gut für die Parallelisierung eignen, d.h. echte Parallelverarbeitung auf einem Multiprozessorsystem. Dies ist allerdings nicht der Fall: Das Problem, daß es nur eine Instanz der Caches oder MMU gibt, existiert bei mehreren CPU-Kernen nicht, da diese alle ihren eigenen Cache besitzen. Im Gegenteil: Dadurch, daß Threads viele Ressourcen gemeinsam nutzen, muss das Betriebssystem durch aufwändige (und langsame) Verwaltung sicherstellen, daß auch alle CPUs im System den gleichen Zustand haben. Wenn z.B. ein Prozess mit mehreren Threads auf einem Multi-Core-System arbeitet und der eine Thread vom Betriebssystem Speicher anfordert, so muß das Betriebssystem synchron alle anderen CPUs anhalten, ihnen den neuen Zustand mitteilen, warten, bis alle den Zustand übernommen haben und kann erst dann weiterarbeiten. F =begin latex \begin{center} \includegraphics{mat2.eps} \end{center} =end latex