5.7 Synchronisation des Threads

# Synchronisation des Threads 

Une fois que vous êtes suffisamment avancés en codage "live" avec un 
certain nombre de fonctions et de threads en simultanéité, vous avez 
probablement remarqué que c'est plutôt facile de faire une faute dans 
l'un des threads et qui lui met fin. Ce n'est pas un gros problème car 
vous pouvez facilement redémarrer le thread en pressant Run. Toutefois, 
quand vous redémarrez le thread, il se trouve alors *déphasé* avec les 
threads d'origine.

## Temps hérité

Comme nous en avons discuté auparavant, les nouveaux threads créés 
avec `in_thread` héritent de tous les paramètres du thread parent. 
Ceci inclut l'heure actuelle. Ce qui signifie que les threads sont 
toujours en phase entre eux quand ils sont démarrés simultanément.

Toutefois quand vous démarrez un thread tout seul, il démarre avec sa 
propre heure qui n'est probablement pas en phase avec l'un quelconque des 
autres threads en cours d'exécution. 

## Cue et Sync

Sonic Pi fourni une solution à ce problème avec les fonctions `cue` et 
`sync`.

`cue` nous permet d'envoyer nos messages de battement de coeur à tous 
les autres threads. Par défaut, les autres threads ne sont pas 
intéressés et ignorent ces messages de battement de coeur. Cependant, 
vous pouvez leur déclarer de l'intérêt avec la fonction `sync`.

La chose importante à laquelle il faut être attentif est similaire à 
`sleep`, en ce sens que cela arrête le thread courant en l'empêchant de 
faire quoi que ce soit pendant un moment. Toutefois, avec `sleep`, vous 
spécifiez combien de temps vous voulez attendre tandis qu'avec `sync`, 
vous ne savez pas combien de temps vous allez attendre - étant donné 
que `sync` attend le `cue` suivant d'un autre thread, ce qui peut être 
court ou long.

Explorons ceci en un peu plus de détail :

```
in_thread do
  loop do
    cue :tick
    sleep 1
  end
end

in_thread do
  loop do
    sync :tick
    sample :drum_heavy_kick
  end
end
```

Nous avons ici deux threads - l'un agissant comme un métronome, ne 
jouant aucun son mais envoyant des messages de battement de coeur 
`tick` à chaque temps. Le second thread se synchronise sur les messages 
`tick` et quand il en reçoit un, il hérite de l'heure du thread `cue` 
et continue de jouer.


Par conséquent, nous entendons l'échantillon `:drum_heavy_kick`  
exactement quand l'autre thread envoie le message `tick`, même si les 
deux threads n'ont pas démarré leur exécution en même temps :

```
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
```

Ce vilain appel à `sleep` mettrait typiquement le second thread en 
déphasage avec le premier. Cependant, comme nous utilisons `cue` et 
`sync`, nous synchronisons automatiquement les threads en évitant un 
décalage de temps accidentel.

## Noms des "Cue"  

Vous êtes libre d'utiliser n'importe quel nom que vous aimeriez pour 
vos messages `cue`. Vous devez juste vous assurer que tout autre thread 
se synchronise sur le bon nom - autrement il attendrait à jamais (au 
moins jusqu'à ce que vous pressiez le bouton Stop).

Jouons avec quelques noms de `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
```

Ici, nous avons une boucle principale `cue` qui envoie aléatoirement un 
des battements de coeur nommés `:foo`, `:bar` ou `:baz`. Nous avons 
aussi ensuite trois boucles en thread se synchronisant sur ces noms 
indépendamment et jouant un échantillon différent. Il est clair 
que nous entendons un son à chaque demi-temps puisque chacun des 
threads `sync` est aléatoirement synchronisé avec le thread `cue` et 
joue son échantillon.

Ceci, bien entendu, marche aussi si vous ordonnez les threads en sens 
inverse puisque les threads `sync` restent en attente du `cue` suivant.
