5.7 Sincronización de hilos

# Sincronización de hilos

Cuando estés lo suficientemente avanzado en live-coding con un número 
de funciones e hilos simultáneamente, notarás que es muy fácil cometer
un error en uno de los hilos y eso lo mata. Eso no es gran cosa, porque
puedes fácilmente recomenzar el hilo con apretar Run. Sin embargo, 
cuando recomiences el hilo estará *out of time* con los hilos actuales.

## Tiempo heredado

Como discutíamos anteriormente, nuevos hilos creados con `in_thread` 
heredan todos los seteos del hilo padre. Esto incluye el tiempo actual,
lo que significa que los hilos estarán siempre en el tiempo con los demás
cuando comenzados simultáneamente.

Sin embargo, cuando comienzas un hilo aparte, comienza con su propio tiempo
que difícilmente estará sincronizado con alguno de los otros que estén 
siendo corridos.

## Cue y Sync (funciones)

Sonic Pi provee una solución a este problema con las funciones `cue` y `sync`.

`cue` permite enviar mensajes de pulso a todos los otros hilos, los cuales, 
en principio no están interesados e ignoran estos mensajes. Sin embargo. puedes
registrar fácilmente su interés con la función `sync`.

Lo importante de notar es que `sync` es similar a `sleep` en que para el hilo actual
por un tiempo. Sin embargo, con `sleep` tu especificas cuánto tiempo va a esperar, 
en cambio con `sync` no sabes cuánto tiempo esperará - ya que `sync` espera por el 
siguiente `cue`  de otro hilo.

Exploremos esto en más detalle:

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

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

Aquí tenemos dos hilos - uno actuando como un metrónomo, no haciendo
sonidos sino que enviando `:tick` mensajes de pulso. El segundo hilo
está sincronizandose con los mensajes de `tick` y cuando recibe uno,
hereda el tiempo del hilo de `cue` y continúa tocando.

El resultado es que escuchamos el sample de `:drum_heavy_kick`
exactamente cuando el otro hilo envía el mensaje `:tick`,
aunque ambos hilos no comenzaron su ejecución al mismo tiempo:

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

Esa llamada `sleep` típicamente haría que el segundo hilo vaya fuera 
de fase con el primero. Sin embargo, por estar usando `cue` y `sync`,
sincronizamos ambos hilos automáticamente.


## Nombres de Cue 

Eres libre de utilizar cualquier nombre que quieras a los mensajes de
`cue` - no sólo `:tick`. Sólo debes asegurarte que los otros hilos estén
`sync` sincronizándose en el nombre correctoe - de otro modo se quedarán
esperando por siempre (o hasta presionar el botón de Parar)

Juguemos con unos cuantos nombres para `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
```

Aquí tenemos un `cue` principal bucleado, el cual envía aleatoriamente
uno de los nombres de pulso `:foo`, `:bar` o `:baz`. Después tenemos
también tres bucles de hilos sincronizándose entre ellos
independientemente y tocando un sampleo diferente. El efecto es que 
escuchamos un sonido cada 0.5 pulsos que cada uno de los hilos está
aleatoriamente sincronizándose `sync` con el hilo de `cue` y toca su
sampleo.

Por supuesto que esto también funciona si ordenas los hilos en reversa,
ya que los hilos `sync` siempre esperarán al siguiente `cue`.
