# Threads synchronisieren

Sobald du ausreichend vertraut damit bist, mit mehreren gleichzeitig ablaufenden Funktionen und Threads live zu programmieren, wirst du bemerken, dass es ziemlich leicht ist in einem einzelnen Thread einen Fehler zu machen, der ihn zum Absturz bringt. Das ist nicht weiter schlimm, da du den Thread ja mit einem Klick auf `Ausführen` einfach neu starten kannst. Wenn du den Thread aber neu startest, dann läuft er nicht mehr im Takt mit den anderen Threads.

## Vererbte Zeit

Wie wir bereits vorher gesehen haben, erben neue Threads die mit `in_thread` erzeugt werden alle ihre Einstellungen von einem Eltern-Thread. Das schließt auch die aktuelle Zeit mit ein. Das bedeutet, dass Threads immer miteinander im Takt sind, wenn sie gleichzeitig gestartet werden.

Wenn du aber einen Thread für sich alleine startest, spielt dieser in seinem eigenen Takt, der wahrscheinlich nicht mit irgendeinem der anderen gerade laufenden Threads synchron ist.

## Cue und Sync

Sonic Pi bietet mit den Funktionen `cue` und `sync` eine Lösung für dieses Problem.

`cue` erlaubt es uns, mit einem Taktgeber regelmäßig Signale an alle anderen Threads zu versenden. Normalerweise zeigen die anderen Threads an solchen Takt-Signalen kein Interesse und ignorieren sie. Mit der `sync`-Funktion kann du jedoch erreichen, dass ein anderer Thread Interesse zeigt.

Wichtig ist dabei sich darüber bewusst zu sein, dass `sync` ähnlich wie `sleep` funktioniert, indem es den aktuellen Thread für eine bestimmte Dauer anhält. Allerdings legst du bei `sleep`fest, wie lange du warten willst, während du bei `sync` nicht weißt, wie lange gewartet werden wird - da `sync` auf den nächsten `cue` eines anderen Threads wartet - was eine kürzere oder längere Dauer sein kann.

Sehen wir uns das im Detail an:

    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 haben wir zwei Threads - einer arbeitet wie ein Metronom, er spielt nichts, aber sendet bei jedem Schlag das Taktgeber-Signal `:tick`. Der zweite Thread synchronisiert sich mit den `tick`-Signalen, und wenn er ein Signal erhält, erbt er dadurch den Takt vom `cue`-Thread und läuft weiter.

Im Ergebnis hören wir das `:drum_heavy_kick`-Sample genau dann, wenn der andere Thread das `:tick`-Signal sendet, auch dann wenn die Ausführung beider Threads gar nicht zur selben Zeit gestartet war:

    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

Der dreiste Aufruf von `sleep` würde normalerweise den zweiten Thread gegenüber dem ersten aus dem Takt bringen. Da wir jedoch `cue` und `sync` verwenden, synchronisieren wir beide Threads automatisch und umgehen dabei ungewollte Abweichungen in der Zeit.

## Cue-Namen

Du kannst deine `cue`-Signale benennen, wie du willst - nicht nur mit `:tick`. Du musst nur sicherstellen, dass jegliche anderen Threads, die sich mit `sync` synchronisieren, auch diesen Namen verwenden - ansonsten werden sie endlos warten (oder zumindest so lange bis du auf `Stopp` klickst).

Lass uns mit ein paar Namen für `cue` spielen:

    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 haben wir eine taktgebende `cue`-Schleife, die auf Zufallsbasis einen der drei Taktgeber-Namen `:foo`, `:bar` oder `:baz` aussendet. Wir haben außerdem drei Schleifen-Threads, die sich unabhängig voneinander, jeder für sich, mit einem der Namen synchronisieren und dann jeweils ein anderes Sample spielen. Im Endeffekt hören wir jeden halben Schlag einen Klang, da jeder der `sync`-Threads mit dem `cue`-Thread auf Zufallsbasis synchronisiert ist und entsprechend sein Sample abspielt.

Das funktioniert natürlich auch, wenn du die Reihenfolge der Threads umkehrst, da die `sync`-Threads einfach dasitzen und auf den nächsten `cue` warten.