# Synchronisation von Threads

Bald wirst Du ausreichend vertraut damit sein, live mit einer Anzahl von gleichzeitig ablaufenden Funktionen und Threads zu coden. Dann wirst Du bemerken, dass es ziemlich leicht ist, einen einzelnen Thread mit einem Fehler zum Abbruch zu bringen. Das ist nicht weiter schlimm, da Du den Thread mit einem Klick auf `Ausführen` ja einfach neu starten kannst. Wenn Du den Thread aber neu startest, dann läuft er nicht mehr zusammen mit den anderen so wie vorher, er ist nun aus dem Takt mit den ursprünglichen Threads.

## Vererbte Zeit

Wir haben zuvor erwähnt, dass neue Threads beim Start mit `in_thread` alle ihre Einstellungen vom Eltern-Thread erben. Das schließt auch die aktuelle Zeit mit ein. Das bedeutet, dass Threads bei gleichzeitigem Start immer miteinander im Takt sind.

Wenn Du aber einen Thread alleine startest, spielt er in seinem eigenen Takt, der höchstwahrscheinlich nicht mit irgendeinem der anderen laufenden Threads zusammenpasst.

## Cue und Sync

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

`cue` erlaubt es uns, regelmäßige Signale an alle anderen Threads mit einem Taktgeber zu versenden. Standardmäßig sind die anderen Threads an solchen Signale nicht interessiert und werden sie ignorieren. Mit der `sync`-Funktion kann man jedoch erreichen, dass ein anderer Thread auf diesen Taktgeber hört.

Eine wichtige Sache, derer man sich bewusst sein sollte: `sync` ist so ähnlich `sleep`, weil es den aktuellen Thread für eine bestimmte Zeit anhält und dieser so lange nichts tut. Allerdings sagt man mit `sleep`, wie lange man warten möchte, während man bei `sync` nicht weiss, wie lange gewartet wird - denn `sync` wartet auf das nächste `cue` eines anderen Threads; das kann kürzer oder länger dauern.

Sehen wir uns das etwas genauer 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 davon arbeitet wie ein Metronom; er spielt nichts, aber sendet bei jedem Schlag ein `:tick`-Taktgebersignal. Der zweite Thread synchronisiert sich mit den `tick`-Signalen, und wenn er eins davon erhält, übernimmt er die Zeit vom `cue`-Thread und läuft weiter.

Als Ergebnis hören wir den `:drum_heavy_kick` Sample genau dann, wenn der andere Thread ein `:tick`-Signal sendet, auch dann, wenn beide Threads gar nicht zur selben Zeit gestartet sind.

    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

Dieser freche (zweite) Aufruf von `sleep` würde eigentlich den zweiten Thread gegenüber dem ersten aus dem Takt bringen. Weil wir aber `cue` und `sync` verwenden, synchronisieren wir beide Threads automatisch und umgehen dabei, dass beide in der Zeit auseinanderfallen.

## Cue benennen

Du kannst `cue`-Signale benennen, wie Du willst - nicht nur mit `:tick`. Du musst nur sicherstellen, dass irgendein anderer Thread sich mit eben diesem Namen synchronisiert - ansonsten wartet der zweite Thread dann für immer (oder jedenfalls bis Du den `Stopp`-Button klickst).

Lasst uns ein paar Namen für `cue` ausprobieren:

    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 `cue`-Hauptschleife, die auf Zufallsbasis einen von drei Namen des Taktgebers `:foo`, `:bar` oder `:baz` aussendet. Wir haben auch drei Schleifen-Threads, die jeder für sich mit einem der Namen synchronisiert werden und dann jeweils ein anderes Sample spielen. Im Endeffekt hören wir bei jedem halben Schlag einen Klang, da jeder der `sync`-Threads auf Zufallsbasis mit dem `cue`-Thread synchronisiert ist, und sein Sample spielt, wenn er dran ist.

Das funktioniert natürlich auch, wenn Du die Reihenfolge der Threads umkehrst: Die die `sync`-Threads sitzen einfach da und warten auf ihren nächsten `cue`.