Historia pytania:
Wsparcie dla pracy z wątkami pojawiło się w Perl 5.005, jednak z powodu specyfiki implementacji języka długo pozostawało eksperymentalne i było obarczone dużą ilością błędów i ograniczeń. Od Perl 5.8 moduł threads (i threads::shared) stał się wystarczająco stabilny do poważnych zadań, jednak model wątków w Perl znacznie różni się od wielu innych języków programowania: każdy wątek otrzymuje swoją kopię wszystkich zmiennych, a jedynie jawnie zadeklarowane struktury przez threads::shared są dostępne do wspólnego użytku.
Problem:
„Zwykłe” zmienne nie są widoczne między wątkami z powodu semantyki copy-on-write. Próba rozdzielenia danych bez threads::shared prowadzi do niesynchronizacji stanu. Przy nieprawidłowym użyciu blokad istnieje ryzyko wyścigów, deadlocków lub niespójnych zmian.
Rozwiązanie:
Aby wspólnie korzystać ze zmiennych, zadeklaruj zmienne jako shared poprzez use threads::shared. Blokuj dostęp do danych wspólnych za pomocą lock, szczególnie jeśli kilka wątków jednocześnie odczytuje/pisze. Do zarządzania cyklem życia wątków używaj metod join/detach. Dla złożonych struktur zadeklaruj każdy element jako shared osobno, ponieważ tylko „górny poziom” nie zapewnia pełnego bezpieczeństwa wątkowego.
Przykład kodu:
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 ";
Kluczowe cechy:
Czy :shared zapewnia bezpieczeństwo wątkowe bez dodatkowego lock?
Nie. Atrybut :shared zapewnia dostęp do zmiennej między wątkami, ale zmiany (na przykład ++ lub --) nie są atomowe. Konieczny jest lock dla każdej sekcji krytycznej.
Czy można podzielić złożoną strukturę (tablica haszy) między wątki jedną dyrektywą :shared?
Nie. Tylko „górny poziom” tablicy lub hasha będzie shared. Każdy zagnieżdżony element również należy zadeklarować jako shared, w przeciwnym razie wewnętrzne struktury nie będą widoczne dla innych wątków.
Czy wątek może zabić inny wątek wywołując exit?
Nie. exit kończy działanie procesu w całości, a nie pojedynczego wątku. Zatrzymanie wątku dokonuje się przez exit wewnątrz wątku lub jest zarządzane logiką join/detach.
Dwa wątki jednocześnie zwiększają $counter :shared bez lock. Ostateczny wynik jest mniejszy od oczekiwanego (typowy problem utraty aktualizacji).
Zalety:
Wady:
Implementacja lock przy każdej zmianie zmiennej wspólnej. Dla dużych struktur zagnieżdżony lock na poziomie elementów.
Zalety:
Wady: