Historia sedna pytania
Wątki (threads) pojawiły się w Perl jako reakcja na potrzebę organizacji obliczeń współbieżnych i równoległej pracy z zasobami w programach wielozadaniowych. Standardowy moduł threads stabilnie wchodzi w Perl od wersji 5.8, stopniowo przechodząc od eksperymentów z ithreads do uniwersalnego threads::shared.
Problem
Pierwsza trudność — Perl nie obsługuje wątków natywnie, tak jak to jest zrealizowane w językach takich jak Java. Wątki w Perl działają na zasadzie kopiowania stosu i danych każdego wątku, co tworzy dodatkowe obciążenie i uniemożliwia bezpośrednią pracę z zmiennymi globalnymi (z wyjątkiem zmiennych z specjalnym powiązaniem przez threads::shared). Perl również nie gwarantuje niezależnej pracy wątków podczas jednoczesnego zapisywania danych bez wyraźnej synchronizacji.
Rozwiązanie
Do organizacji wątków używa się modułu threads oraz dodatkowego modułu threads::shared do wymiany danych między wątkami. Zapewnienie synchronizacji i integralności danych leży na barkach programisty, często poprzez użycie blokad (locks).
Przykład kodu:
use threads; use threads::shared; my $counter :shared = 0; sub increment { lock($counter); $counter++; } my @threads; for (1..10) { push @threads, threads->create(\&increment); } $_->join for @threads; print "Licznik: $counter ";
Kluczowe cechy:
Czy można używać dowolnych zmiennych globalnych bez threads::shared i oczekiwać, że wątki zobaczą zmiany?
Nie. Zmienne globalne są kopiowane do każdego wątku indywidualnie. Wymiana danych — tylko przez threads::shared lub inne IPC (przez procesy).
Czy można uruchamiać fork i wątki w tym samym skrypcie Perl?
Nie zaleca się mieszania forka i wątków, ponieważ prowadzi to do nieprzewidywalnych błędów i niestabilnego zachowania. Perl oficjalnie ostrzega przed jednoczesnym używaniem tych technik.
Czy można przekazywać złożone struktury danych między wątkami przez standardowe referencje?
Nie. Perl nie kopiuje rekurencyjnie zagnieżdżonych struktur automatycznie, a takie próby prowadzą do błędów lub nieintuicyjnych rezultatów. Konieczne będzie głębokie kopiowanie i użycie zasobów shared.
Programista stworzył prostą kolejkę za pomocą tablicy, aktualizowanej jednocześnie z kilku wątków bez threads::shared. Dane często były uszkadzane, wyniki pracy programu były niepoprawne.
Plusy:
Minusy:
Użycie threads::shared z blokadami, cała kolejka została zadeklarowana jako shared, synchronizacja następowała przez blokady. Program działał stabilnie, nawet pod dużym obciążeniem.
Plusy:
Minusy: