Cand ai devenit suficient de avansat sa programezi live cu un numar de functii si thread-uri simultane, ai observat probabil ca este destul de usor sa faci o greseala intr-un thread astfel incat sa il blochezi. Asta nu e o mare problema, fiindca poti reporni usor thread-ul apasand pe Run. Totusi, daca repornesti un thread el va fi decalat fata de thread-urile originale.
Asa cum am discutat anterior, thread-urile noi create cu ‘in_thread’ mostenesc toti parametrii de la thread-ul parinte. Asta include si pozitia in timp. Acest lucru inseamna ca thread-urile sunt mereu aliniate in timp daca au pornit simultan.
Totusi, daca pornesti un thread de sine statator, el va porni cu timpul sau propriu care este putin probabil sa fie sincronizat cu cel al altor thread-uri care ruleaza la momentul respectiv.
Sonic Pi ofera o solutie pentru aceast problema cu functiile ‘cue’ (marcaj de timp) si ‘sync’ (sincronizare).
‘cue’ ne permite sa transmitem mesaje referitoare la tact catre alte thread-uri. Implicit celelalte thread-uri nu sunt interesate si vor ignora aceste mesaje. Totusi, poti sa le determini cu usurinta sa devina interesate folosind functia ‘sync’.
Trebuie sa fii constient ca ‘sync’ este oarecum similar cu ‘sleep’ prin aceea ca opreste thread-ul curent sa faca orice pentru o anumita perioada de timp. Totusi, cu ‘sleep’ tu decizi cat vrei sa dureze pauza, in timp ce cu ‘sync’ nu stii cat timp va dura asteptare, deoarece ‘sync’ asteapta urmatorul ‘cue’ de la alt thread, care poate sosi mai devreme sau mai tarziu.
Sa examinam acest lucru mai in detaliu:
in_thread do
loop do
cue :tick
sleep 1
end
end
in_thread do
loop do
sync :tick
sample :drum_heavy_kick
end
end
Aici avem doua thread-uri, unul actionand ca un metronom, fara sa redea vreun sunet, dar trimitand mesaje ‘:tick’ corespunzator tactului la fiecare bataie. Al doilea thread este sincronizat cu mesajele ‘tick’ si cand primeste unul mosteneste timpul din thread-ul ‘cue’ continuand sa ruleze.
Ca urmare, vom auzi esantionul :drum_heavy_kick
exact cand cealalt thread trimite mesajul ‘:tick’, chiar daca cele doua thread-uri nu au fost pornite in acelasi timp:
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
Acel apel buclucas al functiei ‘sleep’ ar face in mod normal ca al doilea thread sa fie defazat fata de primul. Totusi, folosind ‘cue’ si ‘sync’, putem sincroniza automat thread-urile trecand peste decalajele accidentale.
Esti liber sa folosesti ce nume doresti pentru mesajele ‘cue’, nu doar ‘:tick’. Trebuie doar sa te asiguri ca celelalte thread-uri se sincronizeaza dupa numele corect - in caz contrar ar putea astepta la infinit (sau pana cand apesi butonul Stop).
Sa ne jucam putin cu numele pentru marcajele ‘cue’:
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
Aici avem o bucla ‘cue’ principala care trimite aleator un tact dintre :foo
, :bar
si :baz
. Avem apoi trei thread-uri de tip bucla care se sincronizeaza fiecare cu unul dintre cele trei nume si redau fiecare un alt esantion. Efectul final este ca auzim un sunet la fiecare 0.5 batai deoarece de fiecare data unul dintre thread-urile ‘sync’ se va sincroniza cu thread-ul ‘cue’ si va reda propriul esantion.
Acest lucru va fi valabil desigur si daca schimbi ordinea thread-urilor, deoarece thread-urile ‘sync’ vor sta si vor astepta pur si simplu urmatorul ‘cue’.