5.4 Threads

# Threads[^16]

Mal angenommen, Du hast eine Killer-Basslinie und einen krassen Beat 
gebaut. Wie kannst Du beides zur selben Zeit spielen lassen? Eine 
Möglichkeit ist es, beide Sounds per Hand ineinander zu weben - erst 
spielt der Bass ein bisschen, dann das Schlagzeug, dann der Bass etwas 
mehr... Beides aufeinander zeitlich abzustimmen wird jedoch immer 
schwieriger, vor allem, wenn noch mehr Sounds dazukommen sollen.

Was, wenn Sonic Pi Sounds automatisch für Dich ineinanderflechten 
könnte? Kann es auch, und zwar mit einem besonderen Ding, welches 
*Thread* heisst.

## Unendliche Schleifen

Damit das nächste Beispiel nicht zu kompliziert wird, musst Du Dir 
einfach vorstellen, dass dies Deine Killer-Basslinie und Dein krasser 
Beat sind.

```
loop do
  sample :drum_heavy_kick
  sleep 1
end

loop do
  use_synth :fm
  play 40, release: 0.2
  sleep 0.5
end
```

Wir haben das früher schon besprochen, Endlos-Schleifen sind wie
*schwarze Löcher* für ein Programm; läuft es einmal in die Schleife
kommt es da nicht mehr raus, bis Du den `Stopp`-Button klickst. Wie also 
können wir beide Schleifen zur selben Zeit abspielen? Wir müssen Sonic 
Pi sagen, dass wir einen bestimmten Abschnitt gleichzeitig mit
dem Rest des Codes starten möchten. Hierbei helfen uns Threads.

## Threads kommen zur Hilfe

Wenn wir die erste Schleife in einen `in_thread`-do/end-Block 
hineinpacken, sagen wir Sonic Pi, es soll den Inhalt dieses 
do/end-Blocks *genau zur selben Zeit* wie die nachfolgende Anweisung 
ausführen. Und das ist in diesem Fall die zweite Schleife. Probier es 
aus, und Du wirst den Beat zusammen mit der Basslinie hören!

```
in_thread do
  loop do
    sample :drum_heavy_kick
    sleep 1
  end
end

loop do
  use_synth :fm
  play 40, release: 0.2
  sleep 0.5
end
```

Angenommen, dass wir obendrauf noch einen Synth legen wollten. Ungefähr 
so:

```
in_thread do
  loop do
    sample :drum_heavy_kick
    sleep 1
  end
end

loop do
  use_synth :fm
  play 40, release: 0.2
  sleep 0.5
end

loop do
  use_synth :zawa
  play 52, release: 2.5, phase: 2, amp: 0.5
  sleep 2
end
```

Jetzt haben wir das gleiche Problem wir vorhin. Die erste Schleife wird 
durch das `in_thread` zur selben Zeit wie die zweite gespielt. Aber 
*die dritte Schleife wird nie erreicht*. Also brauchen wir einen 
weiteren Thread:

```
in_thread do
  loop do
    sample :drum_heavy_kick
    sleep 1
  end
end

in_thread do
  loop do
    use_synth :fm
    play 40, release: 0.2
    sleep 0.5
  end
end

loop do
  use_synth :zawa
  play 52, release: 2.5, phase: 2, amp: 0.5
  sleep 2
end
```

## Ablauf in Threads

Was Dich vielleicht erstaunt: Wenn Du den Ausführen-Button klickst, 
erzeugst Du tatsächlich einen neuen Thread, innerhalb dessen der Code 
abläuft. Deshalb entstehen immer neue Soundschichten, wenn Du den 
Ausführen-Button wiederholt klickst. Weil die Abläufe Threads sind, 
werden sie automatisch die Sounds verflechten.

## Geltungsbereich[^17]

Wenn Du Dich besser mit Sonic Pi auskennst, wirst Du herausfinden, dass 
Threads die wichtigsten Bausteine für Deine Musik sind. Eine 
wesentliche Funktion, die Threads haben, ist es, *aktuelle 
Einstellungen*, die für einen Thread gelten, von anderen Threads zu 
isolieren. Was genau bedeutet das? Wenn Du etwa einen Synths mit 
`use_synth` durch einen anderen austauschst, dann veränderst Du den 
Synth lediglich für den *aktuellen Thread* - kein anderer der laufenden 
Threads bekommt den neuen Synth. Sehen wir uns das mal in der Praxis an:

```
play 50
sleep 1

in_thread do
  use_synth :tb303
  play 50
end

sleep 1
play 50
```

Hast Du gehört, dass sich der mittlere Klang von den anderen beiden 
unterschieden hat? Die `use_synth`-Anweisung hat sich nur auf den Thread 
ausgewirkt, in dem sie auch stand und nicht auf den äußeren 
Haupt-Thread.

## Vererbung

Wenn Du einen neuen Thread mit `in_thread` erzeugst, wird dieser vom 
vorherigen alle Einstellungen automatisch erben. Sehen wir uns das an:

```
use_synth :tb303
play 50
sleep 1

in_thread do
  play 55
end
```

Hast Du bemerkt, dass der zweite Ton mit dem `:tb303`-Synth gespielt 
wird, obwohl er in einem anderen Thread läuft? Jede der Einstellungen, 
die mit den unterschiedlichen `use_*`-Ausdrücken vorgenommen wird, 
verhält sich genauso.

Wenn neue Threads starten, erben sie alle Einstellungen von 
ihren Eltern. Wenn Du aber Einstellungen innerhalb dieser neuen Threads 
änderst, haben diese keine Einfluss auf die Eltern-Threads.

## Threads benennen

Schließlich kannst Du Deinen Treads Namen geben.

```
in_thread(name: :bass) do
  loop do
    use_synth :prophet
    play chord(:e2, :m7).choose, release: 0.6
    sleep 0.5
  end
end

in_thread(name: :drums) do
  loop do
    sample :elec_snare
    sleep 1
  end
end
```

Sieh Dir das Protokoll-Fenster an, wenn Du diesen Code laufen lässt. 
Siehst Du, wie die Namen der Threads ausgeben werden?

```
[Run 36, Time 4.0, Thread :bass]
 |- synth :prophet, {release: 0.6, note: 47}
```

## Nur ein Name pro Thread erlaubt

Eine letzte Anmerkungen zu Threads mit Namen: Ein mit einem bestimmten 
Namen benannter Thread kann zur Zeit nur einmal laufen. Probieren wir 
das aus. Sieh Dir den folgenden Code an:

```
in_thread do
  loop do
    sample :loop_amen
    sleep sample_duration :loop_amen
  end
end
```

Kopiere das einmal in einen Buffer und klicke den `Ausführen`-Button. 
Klick in noch ein paarmal. Hör Dir diese Kakophonie mehrerer Amen 
Breaks an, die ryhtmisch nicht unbedingt passend zueinander ablaufen. 
Ok, jetzt kannst Du auf `Stopp` klicken.

Dieses Verhalten konnten wir schon woanders sehen - wenn Du den 
`Ausführen`-Button klickst, legen sich die neuen Klänge über die schon 
laufenden. Wenn eine Schleife abläuft, und Du den `Ausführen`-Button 
dreimal klickst, bekommst Du drei Ebenen mit Schleifen, die gleichzeitig 
ablaufen.

Mit benannten Threads ist das jedoch anders:

```
in_thread(name: :amen) do
  loop do
    sample :loop_amen
    sleep sample_duration :loop_amen
  end
end
```

Versuche mit diesem Code den `Ausführen`-Button mehrmals zu klicken. Du 
wirst immer nur eine Amen-Break-Schleife hören. Das kannst Du auch im 
Protokoll sehen:

```
==> Skipping thread creation: thread with name :amen already exists.
```

Sonic Pi sagt Dir, dass ein Thread mit dem Namen `:amen` bereits läuft 
und es deshalb keinen neuen startet.

Vielleicht erscheint Dir dieses Verhalten im Moment noch nicht sinnvoll 
- aber es wird sehr nützlich sein, wenn wir ins Live-Coding 
einsteigen...

[^16]: Im Deutschen bedeutet *thread* *Kette*, *Faden* oder *Strang*.
    Der *Thread* ist jedoch für Programmiersprachen eine so typische
    Idee, dass eine Übersetzung nur insoweit sinnvoll ist, dass Du Dir
    die Idee, die hinter diesem Begriff steht, besser merken kannst.
    Weil niemand beim Programmieren über eine Kette oder einen Faden
    spricht, werden wir hier nur den englischen Begriff benutzen.

[^17]: *Scope* bedeutet ins Deutsche übersetzt soviel wie
    *Geltungsbereich*. Für die Zukunft ist es aber ganz gut, sich den
    englischen Begriff zu merken, da auch dieser im Programmierbereich
    häufig vorkommt.
