Historie der Frage:
Die Unterstützung für Threads wurde in Perl 5.005 eingeführt, jedoch blieb sie aufgrund der Besonderheiten der Sprachimplementierung lange experimentell und war mit vielen Bugs und Einschränkungen behaftet. Ab Perl 5.8 wurde das Modul threads (und threads::shared) stabil genug für ernsthafte Aufgaben, obwohl das Thread-Modell von Perl stark von vielen anderen Programmiersprachen abweicht: Jeder Thread erhält seine eigene Kopie aller Variablen, und nur ausdrücklich deklarierte Strukturen über threads::shared sind für den gemeinsamen Zugriff verfügbar.
Problem:
„Normale“ Variablen sind aufgrund der Copy-on-Write-Semantik zwischen Threads nicht sichtbar. Der Versuch, Daten ohne threads::shared zu verteilen, führt zu einer Desynchronisierung des Zustands. Bei unsachgemäßer Verwendung von Locks besteht die Gefahr von Race Conditions, Deadlocks oder inkonsistenten Änderungen.
Lösung:
Für den gemeinsamen Gebrauch von Variablen deklariere shared variables über use threads::shared. Sperre den Zugriff auf gemeinsame Daten mit lock, insbesondere wenn mehrere Threads gleichzeitig lesen/schreiben. Verwende die Methoden join/detach zur Verwaltung des Lebenszyklus von Threads. Für komplexe Strukturen deklariere jedes Element separat als shared, da nur die „oberste Ebene“ keine vollständige Thread-Sicherheit gewährleistet.
Beispielcode:
use threads; use threads::shared; my $counter :shared = 0; my @threads; for (1..10) { push @threads, threads->create(sub { for (1..1000) { lock($counter); ++$counter; } }); } $_->join() for @threads; print "Counter: $counter ";
Schlüsselfunktionen:
Gewährleistet :shared die Thread-Sicherheit ohne zusätzliches Lock?
Nein. Das Attribut :shared ermöglicht den Zugriff auf die Variable zwischen Threads, jedoch sind Änderungen (z.B. ++ oder --) nicht atomar. Ein Lock ist für jeden kritischen Abschnitt erforderlich.
Kann eine komplexe Struktur (Array of Hashes) mit einer einzigen Anweisung :shared zwischen Threads geteilt werden?
Nein. Nur die „oberste Ebene“ des Arrays oder Hashes ist shared. Jedes untergeordnete Element muss ebenfalls shared gemacht werden, andernfalls sind innere Strukturen für andere Threads nicht sichtbar.
Kann ein Thread einen anderen Thread durch einen Aufruf von exit beenden?
Nein. exit beendet den gesamten Prozess, nicht einen einzelnen Thread. Das Stoppen eines Threads erfolgt durch exit innerhalb des Threads oder wird durch die Logik von join/detach gesteuert.
Zwei Threads erhöhen gleichzeitig $counter :shared ohne Lock. Das Endergebnis ist geringer als erwartet (typisches Lost Update Problem).
Vorteile:
Nachteile:
Implementierung von Lock bei jeder Änderung der gemeinsamen Variablen. Bei großen Strukturen schrittweises Lock für jedes Element.
Vorteile:
Nachteile: