5.7 Synchronizacja Wątków

# Synchronizacja Wątków

Skoro osiągnąłeś już wystarczająco zaawansowane umiejętności kodowania 
na żywo (live coding) wraz z wykorzystaniem jednocześnie funkcji 
i wątków, na pewno zauważyłeś już, że bardzo łatwo jest popełnić błąd 
w jednym z wątków co powoduje, że zostaje on zabity. To nie jest wielka 
rzecz, ponieważ możesz bardzo łatwo zrestartować wątek poprzez ponowne 
naciśnięcie przycisku Run. Jednakże, kiedy zrestartujesz wątek to wypadnie 
on teraz z rytmu oryginalnych wątków.

## Odziedziczony Czas

Jak już mówiliśmy wcześniej, nowe wątki tworzone z wykorzystaniem polecenia 
`in_thread` dziedziczą wszystkie ustawienia z wątku rodzica. W skład 
tych ustawień wchodzi też aktualny czas. Oznacza to, że wątki pozostają 
zawsze w korelacji czasowej kiedy zostaną uruchomione jednocześnie.

Jendakże, kiedy uruchomisz samodzielny wątek, rozpoczyna się on ze 
swoim własnym czasem i jest mało prawdopodobne, że będzie on w synchronizacji 
z jakimkolwiek innym uruchomionym aktualnie wątkiem.

## Cue (Wskazówka) i Sync (Synchronizacja)

Sonic Pi udostępnia rozwiązanie dla tego problemu za pomocą funkcji 
`cue` (wskazówka) i `sync` (synchronizacja).

`cue` pozwala nam na wysłanie wiadomości o pulsie to wszystkich innych 
aktualnie uruchomionych wątków. Domyślnie inne wątki nie są tym zainteresowane 
i ignorują tę wiadomość o pulsie. Jendakże, możesz bardzo łatwo wywołać 
takie zainteresowanie za pomocą funkcji `sync`.

Ważną rzeczą, której należy być świadomym jest to, że funkcja `sync` 
jest podobna do funkcji `sleep` w taki sposób, że powstrzymuje ona 
aktualny wątek od robienia czegokolwiek przez pewien okres czasu. 
Jednakże, w przypadku funkcji `sleep` musisz zdefiniować jak długo 
chcesz poczekać, natomiast w przypadku funkcji `sync` nie wiesz jak 
długo będziesz chicał poczekać, ponieważ funkcja `sync` czeka na 
następny punkt `cue` z innego wątku, co może nastąpić szybciej lub 
później.

Spróbujmy przyjrzeć się temu trochę bardziej dokładnie:

```
in_thread do
  loop do
    cue :tick
    sleep 1
  end
end

in_thread do
  loop do
    sync :tick
    sample :drum_heavy_kick
  end
end
```

Mamy tutaj dwa wątki - jeden zachowuje się jak metronom, nie gra żadnych 
dźwięków tylko wysyła komunikat `:tik` (cyk) przy każdym uderzeniu. Drugi 
wątek synchronizuje się natomiast przy otrzymaniu każdej kolejnej wiadomości 
`:tick` i kiedy otrzymuje kolejną odziedzicza czas uruchomienia `cue` 
i rozpoczyna swoje działanie.

Jako wynik słyszymy sampel `:drum_heavy_kick` dokładnie w tym samym momencie 
gdy inny wątek wysyła komunikat `:tick`, nawet jeśli obydwa wątki nie 
zostały uruchomione w tym samym czasie.

```
in_thread do
  loop do
    cue :tick
    sleep 1
  end
end

sleep(0.3)

in_thread do
  loop do
    sync :tick
    sample :drum_heavy_kick
  end
end
```

Te niegrzeczne polecenie `sleep` normalnie stworzyłoby drugi wątek, 
który byłby zupełnie niezsynchronizowany z pierwszym. Jednakże, dzięki 
użyciu poleceń `cue` i `sync` automatycznie synchronizujemy wątki blokując 
jednocześnie jakiekolwiek nieprzewidziane rozjazdy w czasie.

## Nazwy dla komunikatów Cue

Dla nazw twoich komunikatów `cue` możesz używać dowolnych nazw - może to 
być nie tylko `:tick`. Musisz się tylko upewnić, że inne wątki 
`sync`hronizują się z właściwą nazwą - w przeciwnym wypadku będą czekać 
w nieskończoność (albo do momentu, w którym naciśniesz przycisk Stop).

Spróbujmy pobawić się z kilkoma komunikatami `cue` o różnych nazwach:

```
in_thread do
  loop do 
    cue [:foo, :bar, :baz].choose
    sleep 0.5
  end
end

in_thread do
  loop do 
    sync :foo 
    sample :elec_beep
  end
end

in_thread do
  loop do
    sync :bar
    sample :elec_flip
  end
end

in_thread do
  loop do
    sync :baz
    sample :elec_blup
  end
end
```

Mamy tutaj główną pętlę `cue`, która co iterację wysyła komunikat z 
losową wybraną nazwą punktu cue do synchronizacji - `:foo`, `:bar` lub 
`:baz`. Następnie mamy trzy wątki z pętlami, które synchronizują się 
na każdym z tych komunikatów niezależnie i odtwarzają różne sample. 
Wynikiem tego jest to, że słyszymy dźwięk co każde 0.5 uderzenia 
jako, że każdy z synchronizowanych (`sync`) wątków jest wybierany 
losowo z wątku `cue` po czym odtwarza swojego sampla.

Oczywiście to wszystko zadziała analogicznie nawet jeśli poukładasz 
wątki w odwrotnej kolejności jako, że wątek `sync` będzie po prostu 
czekał na kolejną wartość wysłaną przez wątek `cue`.
