ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/docs/pws2009/threads.pod
Revision: 1.5
Committed: Sat Jan 17 06:53:25 2009 UTC (17 years, 8 months ago) by root
Branch: MAIN
CVS Tags: HEAD
Changes since 1.4: +3 -2 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 =encoding utf-8
2    
3     =head1 Threads in dynamischen Sprachen und warum Perl-Pseudo-Threads sterben sollten
4    
5     Wie man am Titel sieht, soll es im folgenden um zwei verwandte Themen
6     gehen. Einerseits möchte ich grundsätzliche Gedanken über Threads
7     liefern, einige Vorurteile ausräumen und erläutern, wie Threads in
8     dynamischen Sprachen implementiert werden und warum dies so ist.
9    
10     Andererseits möchte ich erläutern, warum Perl keine Threads unterstützt
11     und weshalb Perls sogenannte Threads eher sterben sollten, da sie
12     mehr Schaden anrichten als sie helfen. Letzteres muss nicht einmal zu
13     Inkompatibilitäten führen: da Perl-Threads keine sind, lassen sie sich
14     effizient ohne emulieren.
15    
16     =head2 Was ist ein Thread - Eine Begriffsbestimmung
17    
18     Am einfachsten erklärt man Threads, in dem man sie Prozessen
19     gegenüberstellt:
20    
21     Prozesse haben (üblicherweise) einen eigenen Adressraum, d.h. Variablen
22     existieren als Kopie in jedem Prozess und sind unabhängig
23     voneinander. Außerdem besitzt ein Prozess eine "Continuation", d.h. im
24     wesentlichen den aktuellen "Ort" der Programmausführung. Unter Unix
25     kommen dann noch andere Ressourcen wie File-Descriptoren hinzu, aber im
26     wesentlichen ist es das.
27    
28     Ein Thread nun ist im wesentlichen ein abgespeckter Prozess (daher werden
29     sie auch oft "LWPs" - Light Weight Processes genannt), bei dem eine oder
30     mehrere dieser Ressourcen gemeinsam genutzt werden, insbesondere der
31     Adressraum. Daher kann man Threads auch als Untereinheit von Prozessen
32     ansehen: ein Prozess kann aus ein oder mehreren Threads bestehen, die sich
33     alle den gleichen Adressraum (die gleichen Variableninhalte) teilen.
34    
35     Bei Threads gibt eine Menge verschiedene Variationen. Die wichtigsten sind:
36    
37     =over 4
38    
39     =item Kooperative Threads
40    
41     Die "Urform" der Threads - diese Threads werden "kooperativ" genannt,
42     nicht, weil sie es sind, sondern, weil sie es sein müssen: Sobald
43     ein Thread ausgeführt wird, läuft dieser ohne Unterbrechung, bis er
44     freiwillig die CPU an einen anderen Thread abgibt. Tut ein Thread dies
45     nicht, läuft nichts anderes im System.
46    
47     Manchmal werden Kooperative Threads auch Koroutinen, Fibers oder
48     Continuations genannt, was genau genommen falsch ist, aber im wesentlichen
49     bezeichnen diese Begriffe dasselbe.
50    
51 root 1.2 Die Hauptvorteile von kooperativen Threads ist die vergleichsweise
52     einfache Programmierung (Race-conditions sind wesentlich einfacher zu
53     vermeiden) und ihre hohe Effizienz. Hauptnachteil ist, daß ein einzelner
54     wildgewordener Thread das gesamte System lahmlegen kann.
55    
56 root 1.1 Windows 3.11, das Oberon-System oder das Coro-Modul von Perl sind
57     Beispiele für kooperatives Threading.
58    
59     =item Preemptive Threads
60    
61     Um das Problem der Monopolisierung des Rechners durch einen unkooperativen
62     (weil z.B. ferhlerhaften) Thread zu umgehen, wurden "preemptive" Threads
63     erfunden: Diese werden ohne ihr Zutun regelmäßig "unterbrochen" (z.B.
64     durch einen Timer) und dadurch von der CPU "verdrängt" (preempted).
65    
66     Diese Art von Threads wird oft auch "time-slicing" (Zeitscheibenverfahren)
67     genannt, da hierbei ein Thread üblicherweise eine bestimmte Zahl von
68     Zeiteinheiten erhält und, wenn er in dieser Zeit die CPU nicht freigibt,
69     wird er automatisch verdrängt.
70    
71 root 1.2 Hauptvorteil von preemptiven Systemen ist, daß man wildgewordene
72     Programme abbrechen kann, d.h. sie legen nicht das gesamte System
73     lahm. Hauptnachteil ist, daß ihre Benutzung sehr kompliziert ist: auch
74     Experten machen regelmäßig Fehler und erzeugen Race-Conditions.
75    
76 root 1.1 Neuere Windows-Versionen, alle Unix-Versionen und insbesondere alle
77     Thread-Systeme dynamischer Sprachen (d.h. Ruby, Python usf.) benutzen
78     dieses Verfahren.
79    
80     =item "Kernel-Threads" und "Userspace-Threads"
81    
82     Ein weiterer wichtiger Unterschied zwischen Thread-Systemen ist, ob sie
83     vom Betriebssystem selbst implementiert werden oder innerhalb eines
84     Prozesses.
85    
86     In letzterem Fall teilen sich alle Threads dieselbe Prozess-Continuation,
87     was nichts anderes bedeutet, als daß diese Threads niemals parallel
88     laufen können (z.B. auf einem SMP-System).
89    
90     Der Begriff "Kernel-Thread" bezeichnet im Gegensatz dazu die Variante, bei
91     er die Threads tatsächlich parallel/gleichzeitig arbeiten können, obwohl
92     dies nicht zwangsweise im "Kernel" implementiert werden muss.
93    
94     Userspace-Threads sind im allgemeinen um ein vielfaches schneller als
95     Kernel-Threads - die Userspace-Threading-Bibliothek, die von Coro benutzt
96     wird schaltet mehr als 100 mal schneller zwischen Threads um als der
97     Linux-Kernel. Selbst auf Perl-Ebene schalten die Userspace-Threads von
98     Coro mehr als 14 mal schneller als die Linux-Kernel-Threads.
99    
100 root 1.2 Kernel-Threads verschärfen gegenüber Userspace-Threads die Problematik
101     der Programmierung nochmals. Ein gutes Beispiel ist MythTV, ein
102     C++-Programm, daß Threads benutzt: auf Single-Core-Systemen arbeitet
103     es meist korrekt, auf Multi-Core-Systemen sind crashes aber an der
104     Tagesordnung, da der Programmierer offenbar nicht damit rechnet,
105     daß Speicherzugriffe nicht mehr atomar sind bzw. die Gefahr einer
106     Race-Condition stark erhöht ist.
107    
108 root 1.1 =back
109    
110     =head2 Wozu wurden Threads erfunden, wozu eignen sie sich?
111    
112     =head3 Single-Cores
113    
114     Multi-Core und Multiprozessorsysteme sind erst seit kurzer Zeit
115     verbreitet. Die überwiegende Mehrheit der Rechner besitzt nach wie vor
116     nur einzelne CPUs.
117    
118     Moderne CPUs besitzen im allgemeinen spezielle Hardware, um Prozesse zu
119     unterstützen, z.B. eine Memory-Management-Unit (MMU) um den Adressraum
120     zu verwalten und einen Translation-Lookaside-Buffer (TLB) um das ganze zu
121     beschleunigen.
122    
123     Da jeder Prozess seinen eigenen Adressraum besitzt, muss die MMU bei jedem
124     Umschalten neu konfiguriert werden, Caches wie der TLB gehen häufig
125     verloren. Dies kann die Performance empfindlich beeinflussen, da die meisten
126     CPUs nur einen Zustand im Cache behalten können.
127    
128     Die natürliche Lösung gegen zu viele Rekonfigurationen sind weniger
129     Rekonfigurationen bei Prozesswechseln. Wechselt man den Adressraum nicht,
130     so erhält man Threads auf natürliche Weise, da die CPU einfach den alten
131     Adressraum weiter benutzt.
132    
133     Daher stammt der Name "Light Weight Processes" (leichtgewichtige
134     Prozesse): Threads werden hier als Prozesse mit weniger "Zustand" behandelt.
135    
136     Dies beschleunigt das Prozessumschalten erheblich, vor allem auf älteren
137     Systemen.
138    
139     =head3 Multiprozessor- oder Multi-Core-Systeme
140    
141     Ein weit verbreiteter Irrtum ist es, daß Threads sich gut für die
142     Parallelisierung eignen, d.h. echte Parallelverarbeitung auf einem
143     Multiprozessorsystem.
144    
145     Dies ist allerdings nicht der Fall: Das Problem, daß es nur eine Instanz
146     der Caches oder MMU gibt, existiert bei mehreren CPU-Kernen nicht, da
147     diese alle ihren eigenen Cache besitzen.
148    
149     Im Gegenteil: Dadurch, daß Threads viele Ressourcen gemeinsam nutzen,
150     muss das Betriebssystem durch aufwändige (und langsame) Verwaltung
151     sicherstellen, daß auch alle CPUs im System den gleichen Zustand haben.
152    
153     Wenn z.B. ein Prozess mit mehreren Threads auf einem Multi-Core-System
154 root 1.2 arbeitet und der eine Thread vom Betriebssystem Speicher anfordert,
155     so muss das Betriebssystem synchron alle anderen CPUs anhalten, ihnen
156     den neuen Zustand mitteilen, warten, bis alle den Zustand übernommen
157     haben und kann erst dann weiterarbeiten. Auf einem Dual-Core-System mag
158     dies noch akzeptabel sein, bei mehr Cores wird dies aber schnell sehr
159     aufwändig, auf Mehrfachprozessorsystemen, bei denen die Kommunikation
160     zwischen CPUs noch länger braucht, wird dies schnell inakzeptabel.
161    
162     Aber auch der ganze normale Speicher-Cache wird nicht effizient
163     genutzt: Da Threads alle Variablen gemeinsam nutzen, liegen gemeinsam
164     genutzte Variablen nicht im lokalen Cache einer CPU, im Gegenteil, sie
165     wandern häufig zwischen den Caches hin- und her, d.h. die Kommunikation
166     läuft über den (vergleichsweise langsamen) Hauptspeicher ab.
167    
168     Letzteres wird häufig dadurch verhindert, daß man unterschiedliche
169 root 1.5 Speicherbereiche in jedem Thread verwendet. Allerdings wird dies durch
170 root 1.2 eine zusätzliche Indirektion bei jedem Zugriff bezahlt. Perls, die diese
171     Methode benutzten, werden dadurch zwischen 15 und ca. 200% langsamer als
172     Perls, die dies nicht tun (auch ohne Parallelbetrieb).
173    
174     Ein Vorteil von Threads ist die vergleichsweise schnelle Kommunikation
175     über den Speicher, was bei Programmen, die viel Kommunikation betreiben,
176     einen großen Gewinn bedeutet, wenn die alternative z.B. Kommunikation über Sockets ist.
177    
178     Moderne Betriebssysteme erlauben es aber ausnahmslos, einzelne
179     Speicherbereiche gemeinsam zu nutzen, so daß Prozesse mit selektiv
180     gemeinsam genutzten Speicher wesentlich effizienter auf Mehr-CPU-Systemen
181     arbeiten, als Threads.
182    
183 root 1.3 =head3 Ein Benchmark: Matrixmultiplikation
184    
185 root 1.2 Am folgenden "Benchmark" wird dies deutlich. Das Benchmark-Programm
186     implementiert eine parallel Matrixmultiplikation, bei der einzelne
187     Zeilen und Spalten an vier "Arbeits-Threads" gesendet werden, die eine
188     Skalarmultiplikation durchführen und an einen Ergebnis-Thread senden.
189    
190     Dabei kann es Perl-Pseudo-Threads (die parallel arbeiten) und Coro-Threads
191     benutzen.
192 root 1.1
193 root 1.2 Das Programm findet sich unter F<http://data.plan9.de/matmult>, ist aber
194     für die Diskussion nicht relevant.
195 root 1.1
196     =begin latex
197    
198     \begin{center}
199     \includegraphics{mat2.eps}
200     \end{center}
201    
202     =end latex
203    
204 root 1.3 =head4 Beschreibung
205    
206 root 1.2 Das doppelt-logarithmische Diagramm stellt die Zahl der Anzahl der
207     Matrixmultiplikationen pro Sekunde der Matrixgröße gegenüber. Die
208     Größe der Matrix hat qualitativ wenig Einfluss, was zählt, ist der
209     relative Unterschied zwischen den Verfahren. Das Testsystem benutzt eine
210     relativ moderne Quad-Core-CPU.
211    
212     Im Diagramm finden sich vier Kurven: die unterste (und langsamste)
213     entspricht den Perl-Pseudo-Threads, die auf allen Cores laufen dürfen und
214     dies auch weitestgehend tun (die CPU-Auslastung betrug ca. 80%). Darüber
215     befindet sich das gleich Programm, allerdings diesmal auf einen einzelnen
216     Core beschränkt (was im Prinzip Unsinn ist, da immer noch die gleiche
217     Anzahl Threads arbeitet).
218    
219     Darüber befinden sich zwei Kurven, die Coro-Threads benutzen. Die obere,
220     langsamere wurde auf einen Perl mit Pseudo-Thread-Support erzeugt,
221     die darunterliegende auf einem Perl ohne, das Programm selbst war
222     identisch. Da Coro-Threads nicht parallel arbeiten, liefen beide auf einem
223     einzigen Core.
224    
225     Was sofort auffällt, ist der starke Unterschied zwischen echter
226 root 1.3 Parallelverarbeitung und kooperativen Coro-Threads: Die Version mit echten
227     Threads ist am rechten Rand mehr als I<326> mal I<langsamer> als die
228     Version die nur eine CPU benutzt.
229    
230     Weniger auffällig, aber umso interessanter ist es, daß das
231     Perl-Thread-Programm auf einer einzelnen CPU fast 20 mal schneller
232     arbeitet als auf 4 Cores, obwohl beide Pseudothread-Varianten identisch
233     sind.
234    
235     Der Unterschied der Coro-Varianten ist in diesem Diagramm nicht sehr
236     deutlich, beträgt immerhin noch zwischen 25% und 35%, im Vergleich zu den
237 root 1.4 Pseudothread-Varianten verschwindend gering (dies ist ein AMD64-Perl, ein
238     x86-Perl hat weitaus höhere Overheads).
239 root 1.3
240     =head4 Diskussion
241    
242     Der Benchmark wurde absichtlich so gewählt, daß die Kommunikation einen
243     entscheidenden Bestandteil bildet.
244    
245     Die Pseudo-Threads von Perl sind hierbei doppelt im Nachteil: einerseits
246     besitzen sie keinen gemeinsamen Adressraum (d.h. Variablen müssen kopiert
247     werden), anderseits ist es durch Threads implementiert, und hat dadurch
248     einen erhöhten Aufwand bei der Verwaltung (doppelte Indirektion).
249    
250     Dies allein kann den gewaltigen Unterschied von 31595% (!) erklären.
251    
252     Der Unterschied zwischen den beiden Pseudothreaded-Kurven ist schwerer zu
253     erklären: Da die Programme identisch sind, muss der Unterschied durch
254     andere Effekte erklärt werden: Die Kommunikation läuft in einem Fall
255     weitestgehend über den CPU-Cache, während dieser bei mehrere Cores
256     nicht benutzt werden kann, hier also die Kommunikation über den L2-Cache
257     (ca. 10 mal langsamer) oder den Hauptspeicher (ca. 50 mal langsamer)
258     abläuft.
259    
260     Hinzu kommt, daß während der Kommunikation gemeinsam genutzte Variablen
261     gegen Zugriffe anderer CPUs geschützt werden müssen. Versuchen
262     mehrere Threads gleichzeitig auf diese zuzugreifen, müssen sie sich
263     synchronisieren. Auch dies läuft über den Hauptspeicher ab und ist noch
264     dazu wesentlich aufwändiger.
265    
266     Der Unterschied von ca. 30% bei den beiden Coro-Varianten erklärt
267     sich alleine dadurch, daß ein Pseudo-Threaded-Perl wesentlich mehr
268     Aufwand treiben muss, um die Adressräume der Threads künstlich
269     auseinanderzuhalten, mithin dupliziert es die Arbeit der MMU die in der
270     CPU eingebaut ist und, obwohl Perl effizienter ist, ist eine Software-MMU
271     natürlich immer noch langsamer als eine gut optimierte Hardwarelösung.
272    
273    
274     =head2 Die Herausforderung: Threads in dynamischen Sprachen
275    
276     Thread-Programmierung ist komplex, vor allem, wenn die verwendeten Threads
277     preemptiv sind oder sogar echt parallel arbeiten. Ein gutes Beispiel ist
278     das "double-checked-locking"-Pattern, daß zehn Jahre in jedem Fachbuch
279     stand, bis man feststellte, daß es nicht stabil arbeitet.
280    
281     Für "hardwarenahe" Sprachen ist es sehr viel einfacher, mit Threads
282     umzugehen: viele Datentypen (z.B. C<int> in C) erlauben atomare Zugriffe,
283     Locking ist explizit, der Zugriff selbst (nur lesend oder verändernd) ist
284     genau festgelegt und so weiter. Und wenn's Crasht dann Crasht's eben - in
285     C ist das nichts ungewöhnliches.
286    
287     Für Sprachen wie Perl oder Python stellt sich das ganze anders
288     dar: Selbst "einfache" Integer-Variablen sind auf C-Ebene durch viele
289     Variablen repräsentiert. Dadurch gibt es keine atomaren Zugriffe, d.h.
290     auch lesend zugreifen kann man nur indem man ein Lock benutzt. Hinzu
291     kommt, daß viele Zugriffe, die man als Programmierer als "Lesezugriff"
292     ansieht, intern tatsächlich Schreibzugriffe auslöst. Ein Beispiel:
293    
294     $a .= $b;
295    
296     Ganz klar, C<$a> wird verändert. Nicht so offensichtlich ist, daß auch
297     C<$b> verändert werden kann, z.B. wenn C<$b> noch nicht als String
298     vorliegt.
299    
300     Da dies interne Details des Interpreters betrifft, ist es für einen Perl-
301     (Python-, Ruby- ...) Programmierer nicht ersichtlich, wann man ein Lock/Mutex
302     benutzen muss und wann nicht.
303    
304     Hinzu kommt, daß Programmierer von dynamischen Sprachen einen gewissen
305     Schutz erwarten: Eine Exception zur Laufzeit ist akzeptabel, ein Crash
306     oder gar Memory-Corruption ist es nicht.
307    
308     =head3 Lösungen: Perl
309    
310     Threads wurden zum ersten (und letzten) Mal in Perl 5.005 eingeführt. Das
311     5.005-Modell implementierte echte Threads, die, sofern die Platform
312     Kernel-Threads unterstützte, auch parallel liefen.
313    
314     Crashes in diesem Modell waren die Tagesordnung. Offenbar war es
315     Perl-Programmierern nicht zu vermitteln, daß man in zwei Threads nicht
316     gleichzeitig (auch nicht lesend) auf dieselbe Variable zugreifen darf.
317    
318     Dies führte dazu, daß dieses Modell in Verruf geriet, obwohl
319     es eigentlich nur das lieferte, was Threads bedeuten: schwierige
320     Programmierung, Crashes und so weiter.
321    
322     Tatsächlich zeigt dies ein generelles Problem bei Threads auf: Sie lassen
323     sich nicht abstrahieren: Stabile Programme erhält man unter anderem
324     durch Abstraktion und Schichtung: einzelne Aufgaben werden in Komponenten
325     abstrahiert (z.B. Module), die die Details regeln, während der Benutzer
326     dieser Komponenten sich nicht um die Details kümmern braucht.
327    
328     Bei Sicherheit funktioniert dies nicht gut: TLS z.B. ist sehr sicher,
329     dennoch darf man (wie Microsoft schon häufig bewiesen hat) nicht naiv
330     davon ausgehen, daß eine Verbindung abhörsicher oder privat wäre, nur,
331     weil man TLS benutzt.
332    
333     Ähnlich ist es mit Threads: sicherlich kann man Queues und ähnliches
334     abstrahieren, aber in vielen Fällen muss man dennoch wissen, wie diese
335     Komponenten implementiert sind, um z.B. Deadlocks zu vermeiden. Und wenn
336     man Daten an andere Threads sendet, muss man sowieso immer selbst den
337     Zugriff regeln, oder selbst Kopien herstellen.
338    
339     Dies ist grundsätzlich unvereinbar mit dem Anspruch, Threads in
340     Hochsprachen sicher gestalten zu können, es sei denn, man belegt alles
341     mit einem Lock. In der Praxis wird die Implementation dadurch aber sehr
342     viel langsamer, was den Vorteil durch Parallelverarbeitung mehr als
343     aufwiegt.
344 root 1.2
345 root 1.3 Das 5.005-Modell verschwand also wieder in der Versenkung, dennoch wollte
346     man irgendwelche "Threads" anbieten.
347 root 1.2
348 root 1.3 =head4 Windows - Wurzel allen Übels
349 root 1.2
350 root 1.3 Und die "Rettung" war Windows. Genaugenommen die Ineffizienz von Windows.
351 root 1.2
352 root 1.4 Dazu muss ich etwas ausholen. Unix (und Perl) haben eine sehr elegante
353     Methode, neue Prozesse zu erzeugen, die man fork+exec nennt. In C (und so
354     ähnlich auch in Perl) sieht das z.B. so aus (ohne Fehlerbehandlung):
355    
356     if (fork () > 0)
357     execl ("/bin/echo", "echo", "Hello, World", (char *)0);
358    
359     Anders als Windows wird das Erzeugen eines neuen Prozesses in zwei Teile
360     zerlegt: zuerst wird eine Kopie des Prozesses erzeugt (fork), dann wird
361     die Kopie durch einen anderen Prozess ersetzt (exec).
362    
363     Das Erstellen einer Kopie ist dank direkter Unterstützung durch die CPU
364     sehr schnell, dennoch mutet es zuerst merkwürdig an.
365    
366     Der Grund, weshalb dies so elegant ist, liegt darin, daß man die Umgebung
367     des neuen Prozesses nicht nur sehr flexibel gestalten kann, sondern
368     auch genau die gleichen Funktionen benutzen kann, die man auch sonst
369     einsetzt. Hier z.B. wird STDIN/STDOUT/SDTERR nach F</dev/null> umgeleitet:
370    
371     if (fork () > 0)
372     {
373     int fd = open ("/dev/null", O_RDWR);
374     dup2 (fd, 0); dup2 (fd, 1); dup2 (fd, 2);
375     execl ("/bin/echo", "echo", "Hello, World", (char *)0);
376     }
377    
378     Obwohl heutige CPUs dieses Verfahren gut unterstützen, erlaubt Microsoft
379     Windows dies nicht.
380    
381     Stattdessen muss man sehr umständliche funktionen mit zig Parametern
382     aufrufen:
383    
384     CreateProcess (NULL, // No module name (use command line).
385     cmdline, // Command line.
386     NULL, // Process handle not inheritable.
387     NULL, // Thread handle not inheritable.
388     FALSE, // Set handle inheritance to FALSE.
389     0, // No creation flags.
390     NULL, // Use parent's environment block.
391     NULL, // Use parent's starting directory.
392     &si, // Pointer to STARTUPINFO structure.
393     &pi) // Pointer to PROCESS_INFORMATION structure.
394    
395     Tatsächlich sind viele dieser Parameter Zeiger auf Strukturen mit sehr
396     vielen weiteren Parametern. Dies liegt daran, daß Windows praktisch
397     jeden möglichen Konfigurationswunsch im Voraus anbieten muss, da man
398     nicht einfach zwischen Prozesserzeugung und Programmstart irgendwelche
399     Funktionen aufrufen kann.
400    
401     (Irgendwie wird einem dabei auch klar, wieso Windows-Programme von so
402     schlechter Qualität sind, wer soll ich den ganzen Misst auch merken
403     können).
404    
405     Nun gut, die native-Win32-Perls standen also vor dem Problem, daß das
406     elegante fork+exec bei Programmierern sehr beliebt war, ihre Platform dies
407     aber nicht unterstützte.
408    
409     Statt wie bei Cygwin eine saubere (und grausam langsame) fork-Emulation
410     zu implementieren, griff man allerdings bei ActiveState zu einem
411     grausamen (und noch langsamerem) Hack: Bei jedem fork wird der komplette
412     Perl-Interpreter mit I<allen> Variablen kopiert.
413    
414     Damit Perl nicht durcheinander kommt, wird I<jeder> Funktion ein
415     zusätzlicher Parameter übergeben, der angibt, wo man die globalen (bzw.
416     gerade nicht mehr globalen) Variablen findet. Dies bedeutet erheblichen
417     zusätzlichen Aufwand, weshalb Perl sehr viel langsamer wird, natürlich
418     auch dann, wenn man keine Threads benutzt.
419    
420     Hinzu kommt, daß das Kopieren sehr komplex und fehlerbehaftet ist
421     (Crashes sind an der Tagesordnung) und natürlich noch langsamer als blind
422     den gesamten Prozess zu kopieren (wie es Cygwin macht).
423    
424     Das schlimmste jedoch ist, daß diese Fork-Emulation auf einen Schlag
425     praktisch alle XS-Module kaputt macht - bis heute funktionieren die
426     meisten XS-Module deshalb nicht sauber unter Windows.
427    
428     Tragisch ist, daß diese fork-Emulation bis heute nicht richtig
429     funktioniert - Memory Leaks und Crashes sind das Ergebnis. Für Windows
430     wahrhaft angemessen, wahrscheinlich fällt Windows-Programmierern dies
431     gar nicht besonders auf.
432    
433     Solange dies auf Windows beschränkt bleibt, ist das natürlich relativ
434     egal. Leider hatte jemand eine geniale Idee:
435    
436     =head4 Wenn wir Threads benutzen um Prozesse zu emulieren...
437    
438     ... dann behaupten wir einfach, unsere Prozesse sind Threads und wir haben
439     Threads.
440    
441     Ich kenne die genauen Details nicht, aber irgendein I<Geisteskranker>
442     fand es cool, alle Perl-Programmierer zu verarschen, Perl überall sehr
443     viel langsamer zu machen und das ganze dann Threads zu nennen: Die
444     Interpreter-Threads waren geboren.
445    
446     Genausowenig, wie Perl kein C ist, obwohl es mit C implementiert ist, sind
447     die Perl-Threads keine Threads, nur weil sie mit Threads implementiert
448     werden.
449    
450     Die sogenannten Perl-Threads sind nichts anderes, als die
451     Windows-Prozess-Emulation auf Unix portiert, mit allen Nachteilen: Nicht
452     nur besitzen die Perl-Pseudo-Threads keinen gemeinsamen Adressraum, sie
453     machen den Perl-Interpreter langsamer und liefern nichts, was jedes
454     Unix nicht wesentlich effizienter mit Hardwareunterstützung erledigen
455     kann. Alle weiteren Effizienzprobleme die durch Kernel-Threads entstehen,
456     sind natürlich auch vertreten. Ohne jeglichen Vorteil aus dem ganzen zu
457     ziehen.
458    
459     Oder doch? Ein genialer Schachzug eigentlich: Man schädigt Perl auf jeder
460     Plattform, auch denjenigen, die dies gar nicht nötig haben, und erhält
461     gleichzeitig ein Buzzword "Thread-Support".
462    
463     Dazu gehört schon Chutzpe.
464    
465     Dies sind die Gründe, weshalb die Perl-Pseudo-Threads sterben sollten:
466     sie verkomplizieren XS-Module unnötig; sind extrem langsam, selbst, wenn
467     man sie gar nicht benutzt; sie funktionieren auf keiner Plattform richtig
468     und vor allem: es sind keine Threads!
469    
470     Wie machen's dann die Anderen?
471    
472     =head3 Python, Ruby und die Anderen
473    
474     Andere Sprachen, die in einer ähnlichen Liga wie Perl spielen, leiden
475     natürlich unter denselben Problemen.
476    
477     Ihre Lösung wird gerne belächelt - Kooperative Threads.
478    
479     Richtig, bei Python läuft nichts parallel. Zumindest sehr wenig. Z.B.
480     I/O. Oder Datenbankzugriffe.
481    
482     Und das sind eigentlich alles die Dinge, die man gerne parallel laufen
483     lassen möchte....
484    
485     Ruby and die meisten anderen dynamischen Sprachen, die Threads
486     implementieren, machen genau dasselbe.
487    
488     Natürlich ist nicht alles perfekt: Python und Ruby benutzen
489     z.B. Kernel-Threads um Userspace-Threads zu emulieren. Dies ist natürlich
490     ineffizient, dafür aber portabel.
491    
492     =head4 Perl kann's auch - Coro
493    
494     Tatsächlich gibt's für Perl auch echte Threads, nämlich das Coro-Modul,
495     das ebenfalls kooperative Threads implementiert. Anders als Python
496     unterstützt es auch schnelle Userspace-Threads (auf den Plattformen, die
497     nicht völlig kaputt sind, d.h. alles außer den BSDs, wo man aufgrund von
498     libc-Bugs langsame OS-Threads benutzen muss).
499    
500     Tatsächlich gibt es Pläne, eine Art Hybrid-Modell zu implementieren: Auf
501     Perl-Ebene hätte man das Python-Thread-Modell, auf C-Ebene kann man sich
502     aber eben mal einen OS-Thread "borgen", um z.B. asynchrones I/O oder
503     asynchrone Datenbank-Zugriffe zu realisieren, wofür aber support der
504     jeweiligen Module notwendig wird.
505    
506     =head3 Erlang
507    
508     Erlang hat auf den ersten Blick noch ein ganz anderes Thread-Modell,
509 root 1.5 nämlich das "echte": "Variablen" sind "geshared" und Threads laufen
510     parallel.
511 root 1.4
512     Allerdings nur auf den ersten Blick: Erlang besitzt gar keine "Variablen",
513     also sind alle Zugriffe erstmal nur Lesend. Außerdem sind die
514     Erlang-Entwickler Profis, was Ausfallsicherheit angeht, und das erreicht
515     man nun mal nur mit Redundanz. Redundanz bedeutet aber auch Abstand,
516     d.h. Erlang-"Threads" müssen auf völlig getrennten Rechnern laufen
517     können.
518    
519     Ergo implementiert Erlang schnelle Userspace-Threads und benutzt für
520     echte Parallelität Prozesse mit nur teilweise gemeinsamen Adressraum, und
521     hat damit alle Vorteile, die man durch Parallelität erreichen kann.
522    
523     Natürlich könnte man die Sprache selbst als gravierenden Nachteil
524     betrachten... Es ist halt nicht Perl.
525    
526     =head2 Schlußwort
527    
528     Zum Wohl aller: Perl-"Threads" müssen sterben. Die Perl-Threads sollen leben!
529 root 1.2