cambios SS 2024-11-25 14:24:09

This commit is contained in:
Jose
2024-11-25 14:24:10 +01:00
parent 4572d4b957
commit b42984ddb3
4 changed files with 90 additions and 13 deletions

View File

@@ -296,4 +296,77 @@ Cada instancia de un thread tiene sus propios recursos, los cuales hay que prote
- Aunque el proceso creado se le llama "hijo" **no hay relación lógica** entre ambos, al menos en Windows. En UNIX/Linux/POSIX **sí hay**.
- La creación del proceso es asíncrona, **NO** es una "llamada".
- En la biblioteca de C: `exec` (UNIX) / `_exec` (Win) funcionan distinto en Windows y UNIX/Linux. En Windows se basa en `CreateProcess` y en UNIX/Linux el proceso actual carga un nuevo programa:
- `posix spawn` es lo equivalente a `CreateProcess` que básicamente hace: `fork` + `exec`
- `posix spawn` es lo equivalente a `CreateProcess` que básicamente hace: `fork` + `exec`
# <mark style="background: #FFF3A3A6;">TEMA 6: Control de concurrencia y sincronización</mark>
En el tema anterior se trataba como lanzar varios hilos que cooperan para una tarea. También vimos el mecanismo más básico de sincronización: la espera activa. Sin embargo, hay técnicas más complejas.
## <mark style="background: #ADCCFFA6;">1. Control de concurrencia y sincronización</mark>
Cuando varios hilos cooperan para realizar un trabajo es necesario coordinarlos. Los mecanismos para coordinarlos se pueden dividir en dos grupos:
### <mark style="background: #FFB86CA6;">Control de concurrencia</mark>
Hay al menos un recurso compartido por varios hilos (variables globales, buffers, la consola, etc.). Esto puede llevar a conflictos, por ejemplo en el uso de la consola:
- Un `printf` no habría problema.
- Si son varios la secuencia de ejecución no se asegura.
- Es complicado usar `getchar`, `scanf` y demás funciones I/O desde varios hilos en ejecución.
### <mark style="background: #FFB86CA6;">Sincronización</mark>
Uno o varios hilos deben esperar a que otro/s terminen alguna tarea. Ya hemos visto uno de ellos:
- `WaitForSingleObject(HandleThread, ...)` espera a que un hilo termine.
- `std::thread.join` versión muy simplificada, equivalente a `WaitForSingleObject(pth -> native_handle(), INFINITE);`
## <mark style="background: #ADCCFFA6;">2. Sincronización entre hilos</mark>
### <mark style="background: #FFB86CA6;">Secciones críticas</mark>
Es el mecanismo de control de concurrencia mas fácil. Se trata básicamente de:
- Definir una variable tipo `CRITICAL_SECTION` (normalmente global).
- Identificar las zonas de código que usan un recurso compartido por distintos hilos.
- "Marcarlas" como secciones críticas:
- `EnterCriticalSection()` al inicio
- `LeaveCriticalSection()` al final
- Antes de usar las funciones anteriores debemos asegurarnos de inicializar la sección crítica con `InitializeCriticalSection()` y cuando ya no se vaya a usar más de borrarla con `DeleteCriticalSection()`.
<div class="nota"><h4>NOTA</h4><p><code>EnterCriticalSection()</code> bloquea sin timeout. Una vez bloquea, el hilo permanece bloqueado hasta que la sección crítica se libere. Esto hace que los errores sean difíciles de tratar. Se puede subsanar con <code>TryEnterCriticalSection()</code></p></div>
### <mark style="background: #FFB86CA6;">Mútex</mark>
El funcionamiento es similar a las secciones críticas. Algunas diferencias son:
- Tienen nombre y se pueden usar para coordinar hilos en **distintos procesos**.
- Permiten timeout.
Se usan de la siguiente manera:
- El mútex debe estar creado antes de usarlo.
- `WaitForSingleObject(HandleMutex, ...)` antes de usar el recurso compartido. La función bloquea el hilo si otro ya "posee" el mútex. Cuando se desbloquea puede devolver `WAIT_OBJECT_0` indicando éxito (y el hilo captura el mútex) o un error.
- `ReleaseMutex()` después de usar el recurso compartido.
### <mark style="background: #FFB86CA6;">Funciones <code>WaitFor</code></mark>
Hemos visto que `WaitForSingleObject()` se bloquea hasta que un hilo termine o el hilo capture el mútex. En general, se llama objetos con bloqueo (waitable objects) a aquellos elementos del SO sobre los que se puede usar una función `WaitFor`.
<div class="nota"><h4>NOTA</h4><p>Un waitable object puede estar en el estado <code>signaled</code> (señalado) o <code>nonsignaled</code> (no señalado)</p></div>
La función se bloquea si el objeto está `nonsignaled`.
### <mark style="background: #FFB86CA6;">Eventos</mark>
Los eventos son "notificaciones" de que ha pasado algo en el SO. Se usan de la siguiente manera:
- El evento debe estar creado antes de usarlo.
- `CreateEvent()`
- `WaitForSingleObject(HandleEvento, ...)` se bloquea si el evento está `nonsignaled`. Cuando se desbloquea puede devolver éxito (`WAIT_OBJECT_0`) si está `signaled` o error (`WAIT_TIMEOUT`,`WAIT_ABANDONED`,`WAIT_FAILED`).
- Manipular el evento:
- `SetEvent()` marca el evento como `signaled`
- `ResetEvent()` marca el evento como `nonsignaled`
- `PulseEvent()` desbloquea hilo/s esperando en el momento de ejecutarse y el evento pasa a `nonsignaled`.
![[Pasted image 20241125141145.png]]
### <mark style="background: #FFB86CA6;">Semáforos</mark>
Mucho menos usados en Windows que en UNIX/Linux. Un semáforo tiene un contador interno.
- El semáforo está abierto (`signaled`) si el contador es mayor que 0.
- El semáforo está cerrado (`nonsignaled`) si el contador es 0.
Se usan de la siguiente manera:
- El semáforo se debe crear antes de usarlo con `CreateSemaphore()`.
- `WaitForSingleObject(HandleSemaforo, ...)` se bloquea si el semáforo está cerrado (`nonsignaled`). Cuando se desbloquea puede devolver:
- `WAIT_OBJECT_0` el semáforo está abierto. Decrementa el contador en 1 y si llega a 0 el semáforo se cierra.
- Otro valor indicando error.
- `ReleaseSemaphore()` incrementa el contador en 1
### <mark style="background: #FFB86CA6;">Otros mecanismos</mark>
- **Interlock:** Se usan en variables compartidas. Permiten incrementar/decrementar una variable sin peligro. Son las más rápidas (frecuentes en drivers).
- **Mensajes:** Implementación rara en Windows pero muy comunes en otros SO. Muy útiles para intercambiar y sincronizar datos. En Windows se usan para el sistema de ventanas.
## <mark style="background: #ADCCFFA6;">3. Sincronización entre procesos</mark>
Es posible usar mútex/eventos/semáforos entre procesos al igual que hacemos con los hilos.
### <mark style="background: #FFB86CA6;">Sincronización + datos (named pipes)</mark>
Una named pipe permite enviar datos de forma sincronizada entre dos o más procesos. Las principales características son:
- Tubería de mensajes: los mensajes enviados por un lado se reciben por el otro en el mismo orden.
- Bidireccional: permite a un proceso enviar/recibir con la misma tubería.
- Utiliza el paradigma C-S.
<div class="nota">
<h4>Ejemplo típico: named pipe</h4>
<ul>
<li>El servidor crea un objeto de sincronización para indicar que se ha creado la named pipe</li>
<li>Cada cliente crea su propio objeto de sincronización con el mismo parámetro <code>Name</code> para ver si la named pipe ha sido creada, y luego se bloquea esperando a que la tubería esté creada.</li>
<li>Tras crear la tubería, el servidor indica que la tubería existe con el evento de sincronización</li>
</ul>
</div>