diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index d088028..4707ebf 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -4,19 +4,21 @@ "type": "split", "children": [ { - "id": "56e813a421587f25", + "id": "2d0b9251619569b8", "type": "tabs", "children": [ { - "id": "75fd2cd48c9b7660", + "id": "f159ed47981f3b40", "type": "leaf", "state": { - "type": "image", + "type": "markdown", "state": { - "file": "TERCERO/ATR1/images/Captura de pantalla de 2024-11-20 10-54-39.png" + "file": "TERCERO/SS/SS 24-25.md", + "mode": "source", + "source": false }, - "icon": "lucide-image", - "title": "Captura de pantalla de 2024-11-20 10-54-39" + "icon": "lucide-file", + "title": "SS 24-25" } } ] @@ -75,7 +77,8 @@ } ], "direction": "horizontal", - "width": 300 + "width": 300, + "collapsed": true }, "right": { "id": "44cf06183e1c1c7d", @@ -172,10 +175,14 @@ "obsidian-git:Open Git source control": false } }, - "active": "75fd2cd48c9b7660", + "active": "f159ed47981f3b40", "lastOpenFiles": [ - "Untitled.md", + "Pasted image 20241125141145.png", + "Pasted image 20241125141142.png", + "TERCERO/SPD/Teoría_2425.md", + "TERCERO/SS/SS 24-25.md", "TERCERO/ATR1/images/Captura de pantalla de 2024-11-20 10-54-39.png", + "Untitled.md", "TERCERO/ATR1/Resolución 1 Parcial ATR1.md", "TERCERO/IA/images/Pasted image 20241115112854.png", "TERCERO/IA/images/Pasted image 20241115110324.png", @@ -186,14 +193,11 @@ "conflict-files-obsidian-git.md", "TERCERO/IA/Teoría_2425.md", "TERCERO/SPD/P4_SPD.md", - "TERCERO/SS/SS 24-25.md", "TERCERO/SS/SS Lab.md", - "TERCERO/SPD/Teoría_2425.md", "TERCERO/SS/images/Pasted image 20241024113018.png", "TERCERO/SS/images/Pasted image 20241024090239.png", "TERCERO/SPD/images/Pasted image 20241022150214.png", "TERCERO/ATR1/images/Pasted image 20241020204947.png", - "TERCERO/ATR1/images/Pasted image 20241020204701.png", "SEGUNDO/ADDA/Teoría_2324.md", "TERCERO/IA/Apuntes Julia.md", "SEGUNDO/IISSI2/Teoría_2324.md", diff --git a/Pasted image 20241125141142.png b/Pasted image 20241125141142.png new file mode 100644 index 0000000..1019cbf Binary files /dev/null and b/Pasted image 20241125141142.png differ diff --git a/Pasted image 20241125141145.png b/Pasted image 20241125141145.png new file mode 100644 index 0000000..1019cbf Binary files /dev/null and b/Pasted image 20241125141145.png differ diff --git a/TERCERO/SS/SS 24-25.md b/TERCERO/SS/SS 24-25.md index 2c686dc..6cea218 100644 --- a/TERCERO/SS/SS 24-25.md +++ b/TERCERO/SS/SS 24-25.md @@ -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` \ No newline at end of file + - `posix spawn` es lo equivalente a `CreateProcess` que básicamente hace: `fork` + `exec` +# TEMA 6: Control de concurrencia y sincronización +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. +## 1. Control de concurrencia y sincronización +Cuando varios hilos cooperan para realizar un trabajo es necesario coordinarlos. Los mecanismos para coordinarlos se pueden dividir en dos grupos: +### Control de concurrencia +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. +### Sincronización +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);` +## 2. Sincronización entre hilos +### Secciones críticas +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()`. +

NOTA

EnterCriticalSection() 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 TryEnterCriticalSection()

+### Mútex +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. +### Funciones WaitFor +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`. +

NOTA

Un waitable object puede estar en el estado signaled (señalado) o nonsignaled (no señalado)

+La función se bloquea si el objeto está `nonsignaled`. +### Eventos +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]] +### Semáforos +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 +### Otros mecanismos +- **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. +## 3. Sincronización entre procesos +Es posible usar mútex/eventos/semáforos entre procesos al igual que hacemos con los hilos. +### Sincronización + datos (named pipes) +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. +
+

Ejemplo típico: named pipe

+ +