Het Synchroniseren van Threads

Eens je het live coderen voldoende onder de knie hebt met een aantal functies en threads tezelfdertijd, dan heb je misschien opgemerkt dat het niet moeilijk is om een fout te maken in één van de threads die dodelijk kan zijn. Niet heel erg, want je kan makkelijk de thread terug herstarten door op afspelen te klikken. Maar als je de thread herstart is deze uit de maat met de originele threads.

Overgenomen Tijd

Zoals we eerder hebben besproken, erven nieuwe threads gemaakt met in_thread alle instellingen van de bovenliggende thread. Dus ook de huidige tijd. Dit betekent dat de threads altijd synchroon met elkaar zijn als zij tegelijkertijd zijn gestart.

Wanneer je een thread op zich start, begint het met zijn eigen klok die waarschijnlijk niet in sync is met een van de andere actieve threads.

Cue en Sync

Sonic Pi biedt een oplossing voor dit probleem met de functies ‘cue’ en ‘sync’.

‘cue’ laat ons toe, hartslag-berichten te sturen aan alle andere threads. Standaard zijn de andere threads niet geïnteresseerd in deze, en negeren deze hartslag-berichten. Je kan hun aandacht wel vragen met de functie ‘sync’.

Het belangrijkst om weten is dat ‘sync’ vergelijkbaar is met sleep in die zin dat het de huidige thread stopt voor een periode van tijd om om actief te zijn. Met sleep geef je echter aan hoe lang je wilt wachten terwijl je met ‘sync’ niet weet hoe lang je zal wachten - als ‘sync’ op de volgende ‘cue’ van een andere thread wacht die lang of kort kan wegblijven.

Laten we dit in detail bekijken:

in_thread do
  loop do
    cue :tick
    sleep 1
  end
end
in_thread do
  loop do
    sync :tick
    sample :drum_heavy_kick
  end
end

Hier hebben we twee threads - één gedraagt zich als metronoom, zonder geluid te maken en zend elke maat enkel :tick hartslag-berichten uit. De tweede thread synchroniseerd op tick berichten en wanneer deze er één ontvangt, erft hij de cue tijd thread en blijft lopen.

Als gevolg zullen wij de ‘: drum_heavy_kick’ sample horen, precies op het moment dat de andere thread het :tick bericht uitzend, zelfs als de twee threads niet met de uitvoering ervan op hetzelfde moment beginnen:

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

Die ondeugende sleep aanroep zou doorgaans de tweede thread uit fase laten lopen met de éérste. Als we echter cue en sync gebruiken, synchroniseren we automatisch alle lopende threads die toevallige verschuivingen in timing omzeilen.

Cue Namen

Je mag eender welke naam gebruiken voor jouw cue berichten, niet enkel :tick. Je hoeft er alleen voor de zorgen dat alle threads op de correcte naam sync-en, anders gaan ze voor eeuwig wachten (of tot je op de stopknopstop klikt tenminste).

Laten we met en paar cue namen spelen:

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

Hier hebben we een hoofd cue loop die random de heartbeat namen :foo, :bar or :bazuitzend. We hebben ook drie loop threads die elk onafhankelijk op één van die namen synct en daarbij een verschillende sample afspeeld. Het netto effect is dat we elke 0.5 beat een klank horen als wordt elk van deze 0.5 sync threads random ge-synct wordt met de cue thread en zijn sample speelt.

Dit werkt natuurlijk ook als jen de threads omgekeerd ordend omdat desync threads gewoon op de volgende cue zitten wachten.