Concurrencia y sincronización — ejercicios tipo examen con condiciones de carrera, semáforos paso a paso y la leche de siempre (y un Peterson que no te deja entrar)

En el primer simulacro de este bloque me encontré una pregunta de test-and-set y contesté sin pensar que «devuelve true si consigue entrar». Era justo al revés: devuelve el valor antiguo, y true significa que alguien está dentro. Desde entonces, en concurrencia leo cada definición dos veces antes de marcar nada.

Este artículo es para eso: practicar con preguntas como las del examen hasta que las definiciones salgan solas. Si aún no has leído la teoría, empieza por concurrencia y sincronización: aquí ya no se explica desde cero, aunque cada pregunta trae su explicación. Y si quieres ver antes las trazas resueltas paso a paso, tienes la práctica guiada de concurrencia.

Cómo usar este artículo

  1. Haz el test sin mirar la teoría. Cada pregunta tiene su botón «Comprobar»: así ves la explicación justo cuando la necesitas.
  2. Las múltiples penalizan, como en el examen: cada opción falsa marcada resta. Si dudas de una, piensa si de verdad la marcarías en el examen.
  3. Las trazas se hacen con papel. Antes de rellenar las casillas, apunta en una hoja el valor del semáforo y el estado de cada proceso paso a paso.
  4. Las de desarrollo, escríbelas enteras y compáralas después con la respuesta modelo y con la lista de lo que se valora. Al final tienes el chuletario para repasar.

Test: concurrencia, sección crítica y sincronización

Treinta preguntas con todos los formatos del examen: única, múltiple que penaliza, verdadero o falso, huecos, relacionar, ordenar y respuesta corta. Cubren el tema entero, desde la definición de concurrencia hasta los filósofos.

Pregunta 1Única

Tu portátil tiene un solo núcleo y tienes abiertos onedrive, el campus y spotify. ¿Cuál de estas frases es correcta?

  1. Los tres procesos son concurrentes aunque nunca se ejecuten dos instrucciones suyas en el mismo instante
  2. No hay concurrencia porque solo hay un núcleo
  3. Hay paralelismo, porque los tres avanzan a la vez
  4. Solo hay concurrencia si los tres procesos comparten datos
Solución

Respuesta: Los tres procesos son concurrentes aunque nunca se ejecuten dos instrucciones suyas en el mismo instante

Concurrencia es la existencia simultánea de varios procesos en ejecución. No implica ejecución simultánea: con un núcleo, los procesos se intercalan.

Pregunta 2Verdadero o falso

Concurrencia, paralelismo y computación distribuida. Indica si es verdadero o falso:

  1. El paralelismo es un caso particular de la concurrencia.
  2. Si dos procesos son concurrentes, se están ejecutando en dos procesadores distintos.
  3. Para que haya paralelismo hacen falta varios procesadores (o núcleos).
  4. La computación distribuida es concurrencia en un único procesador muy rápido.
Solución
  • V — El paralelismo es un caso particular de la concurrencia.
  • F — Si dos procesos son concurrentes, se están ejecutando en dos procesadores distintos.
  • V — Para que haya paralelismo hacen falta varios procesadores (o núcleos).
  • F — La computación distribuida es concurrencia en un único procesador muy rápido.

Todo paralelismo es concurrencia, pero no al revés. Paralelismo = ejecución simultánea en varios procesadores; si esos procesadores están en distintas máquinas unidas por una red, hablamos de computación distribuida.

Pregunta 3Rellenar huecos

Completa con la palabra que falta:

Cuando varios procesos existen a la vez hablamos de ____. Si además se ejecutan a la vez en varios procesadores, es ____. Y si esos procesadores están repartidos por una ____, la computación es ____.

Palabras:

  • red
  • multiprogramación
  • concurrencia
  • distribuida
  • caché
  • secuencial
  • paralelismo
Solución

Cuando varios procesos existen a la vez hablamos de concurrencia. Si además se ejecutan a la vez en varios procesadores, es paralelismo. Y si esos procesadores están repartidos por una red, la computación es distribuida.

De más general a más concreto: concurrencia → paralelismo → computación distribuida (paralelismo en red).

Pregunta 4Múltiple · penaliza

¿Con qué mecanismos puede un programador expresar la concurrencia en su código?

  1. Llamando a una API de hilos, como pthreads
  2. Con objetos concurrentes que ofrece el lenguaje
  3. Con sentencias concurrentes del propio lenguaje
  4. Declarando las variables como globales
  5. Aumentando el tamaño de la caché
  6. Guardando los datos en un fichero compartido
Solución

Respuesta: Llamando a una API de hilos, como pthreads · Con objetos concurrentes que ofrece el lenguaje · Con sentencias concurrentes del propio lenguaje

Tres maneras: una API (pthreads), objetos concurrentes (Java, C++) y sentencias concurrentes (Ada, Go…).

Pregunta 5Múltiple · penaliza

¿Para qué sirve la programación concurrente?

  1. Para describir de forma natural actividades del mundo real que ocurren a la vez
  2. Para marcar trozos de código que pueden ir en paralelo y aprovechar varios procesadores
  3. Para escribir con más facilidad ciertos algoritmos
  4. Para que el programa dé siempre el mismo resultado en cada ejecución
  5. Para no tener que proteger los datos compartidos
  6. Para tener más dispositivos de entrada y salida
Solución

Respuesta: Para describir de forma natural actividades del mundo real que ocurren a la vez · Para marcar trozos de código que pueden ir en paralelo y aprovechar varios procesadores · Para escribir con más facilidad ciertos algoritmos

Expresar mejor el mundo real, aprovechar varios procesadores y simplificar algunos algoritmos. El precio: resultados que pueden depender del intercalado.

Pregunta 6Única

¿Por qué un fallo de concurrencia es tan difícil de reproducir al depurar?

  1. Porque el resultado depende de cómo se intercalen las instrucciones de los hilos, y ese intercalado cambia de una ejecución a otra
  2. Porque el compilador elimina las variables compartidas
  3. Porque los hilos no se pueden depurar con un depurador
  4. Porque solo ocurre en ordenadores con un núcleo
Solución

Respuesta: Porque el resultado depende de cómo se intercalen las instrucciones de los hilos, y ese intercalado cambia de una ejecución a otra

Un programa concurrente tiene que funcionar con todos los intercalados posibles; probarlo diez veces y que salga bien no demuestra nada.

Pregunta 7Respuesta corta

¿Cómo se llama la situación en la que el resultado de un programa depende del orden en que los hilos acceden a un dato compartido?

Solución

Respuesta: Condición de carrera

Es una condición de carrera: dos hilos «corren» hacia el mismo dato y gana quien escribe el último.

Pregunta 8Única

visitas vale 10 y dos hilos ejecutan visitas = visitas + 1 (leer, sumar, escribir). El planificador hace: lee H1, lee H2, suma H1, suma H2, escribe H1, escribe H2. ¿Cuánto vale visitas al final?

  1. 11
  2. 12
  3. 10
  4. Depende del número de núcleos
Solución

Respuesta: 11

H1: 10 → 11. H2 también leyó 10 antes de que H1 escribiera: 10 → 11. El segundo escribe pisa el 11 con otro 11. Se pierde una actualización.

Pregunta 9Ordenar

Ordena lo que hace la CPU para ejecutar contador = contador + 1:

  1. Guardar el registro en la posición de memoria de contador
  2. Cargar el valor de contador desde la memoria a un registro
  3. Sumar 1 al registro
Solución

Orden correcto:

  1. Cargar el valor de contador desde la memoria a un registro
  2. Sumar 1 al registro
  3. Guardar el registro en la posición de memoria de contador

Leer (lw), sumar (addi) y escribir (sw). Si el planificador expulsa al hilo entre el primer y el último paso, otro hilo puede leer un valor viejo.

Pregunta 10Única

¿Qué es una sección crítica?

  1. El trozo de código de un proceso o hilo donde accede a datos que comparte con otros
  2. La parte del sistema operativo que se ejecuta en modo núcleo
  3. Cualquier bucle infinito de un programa
  4. El código que más tiempo de CPU consume
Solución

Respuesta: El trozo de código de un proceso o hilo donde accede a datos que comparte con otros

Sección crítica: segmento de código donde se accede a datos compartidos. Requisito básico: exclusión mutua, nunca más de un proceso o hilo dentro.

Pregunta 11Múltiple · penaliza

¿Qué tres condiciones debe cumplir una solución al problema de la sección crítica?

  1. Exclusión mutua
  2. Progreso
  3. Espera limitada
  4. Espera activa
  5. Prioridad del proceso más corto
  6. Interbloqueo
Solución

Respuesta: Exclusión mutua · Progreso · Espera limitada

Exclusión mutua (uno dentro como mucho), progreso (si está libre, se decide pronto y solo entre los que quieren entrar) y espera limitada (nadie se cuela delante de ti sin límite).

Pregunta 12Relacionar

Relaciona cada requisito con su significado:

Exclusión mutua
Progreso
Espera limitada

Definiciones:

  • Si nadie está dentro y alguien quiere entrar, la decisión la toman solo los interesados y en un tiempo finito
  • Hay un máximo de veces que otros pueden entrar antes que un hilo que ya lo ha pedido
  • Nunca hay más de un hilo dentro de la sección crítica
  • El hilo que entra primero sale el último
Solución
  • Exclusión mutua → Nunca hay más de un hilo dentro de la sección crítica
  • Progreso → Si nadie está dentro y alguien quiere entrar, la decisión la toman solo los interesados y en un tiempo finito
  • Espera limitada → Hay un máximo de veces que otros pueden entrar antes que un hilo que ya lo ha pedido

La exclusión mutua protege el dato; el progreso y la espera limitada evitan que alguien se quede esperando para siempre.

Pregunta 13Verdadero o falso

Proteger la sección crítica inhibiendo las interrupciones. Verdadero o falso:

  1. Mientras un proceso está en su sección crítica, ningún otro proceso del sistema puede avanzar en esa CPU.
  2. Funciona igual de bien en un multiprocesador.
  3. Garantiza la espera limitada.
  4. Impide que el temporizador expulse al proceso mientras toca el dato compartido.
Solución
  • V — Mientras un proceso está en su sección crítica, ningún otro proceso del sistema puede avanzar en esa CPU.
  • F — Funciona igual de bien en un multiprocesador.
  • F — Garantiza la espera limitada.
  • V — Impide que el temporizador expulse al proceso mientras toca el dato compartido.

Sin interrupciones no hay expulsión, así que en un solo procesador hay exclusión mutua. Pero congela toda la concurrencia, no sirve con varios procesadores (cada uno tiene sus interrupciones) y no garantiza espera limitada.

Pregunta 14Relacionar

Relaciona cada instrucción o idea con su descripción:

Instrucción atómica
Test-and-set
Compare-and-swap
LOCK BTS

Definiciones:

  • Pone la variable a verdadero y devuelve el valor que tenía antes
  • Si la variable vale lo esperado, le pone un valor nuevo y devuelve verdadero
  • Versión de x86: la CPU se queda el bus mientras hace el test-and-set
  • Se ejecuta entera sin que nada la interrumpa
  • Desactiva las interrupciones de todos los procesadores a la vez
Solución
  • Instrucción atómica → Se ejecuta entera sin que nada la interrumpa
  • Test-and-set → Pone la variable a verdadero y devuelve el valor que tenía antes
  • Compare-and-swap → Si la variable vale lo esperado, le pone un valor nuevo y devuelve verdadero
  • LOCK BTS → Versión de x86: la CPU se queda el bus mientras hace el test-and-set

Las tres son ayudas del hardware: leen y modifican una celda de memoria en un solo paso indivisible. IBM ya tenía en los años 60 un SWAP atómico; MIPS y ARM usan la pareja LL/SC.

Pregunta 15Única

Un hilo ejecuta while (test_and_set(ocupado)) {} y en ese momento ocupado vale true. ¿Qué pasa?

  1. La llamada devuelve true, ocupado sigue a true y el hilo da otra vuelta esperando
  2. La llamada devuelve false y el hilo entra en la sección crítica
  3. La llamada pone ocupado a false para que entre el siguiente
  4. El hilo se bloquea y deja libre la CPU
Solución

Respuesta: La llamada devuelve true, ocupado sigue a true y el hilo da otra vuelta esperando

test-and-set devuelve el valor antiguo. Si era true, alguien está dentro y el bucle sigue girando (espera activa). Solo entra quien recibe false.

Pregunta 16Única

Un semáforo vale 0 y hay dos procesos bloqueados en su cola. Otro proceso hace signal. ¿Qué ocurre, con la definición de los apuntes?

  1. Se despierta uno de los dos bloqueados y el semáforo sigue valiendo 0
  2. El semáforo pasa a valer 1 y los dos siguen bloqueados
  3. Se despiertan los dos y el semáforo vale 1
  4. El proceso que hace signal se bloquea
Solución

Respuesta: Se despierta uno de los dos bloqueados y el semáforo sigue valiendo 0

signal despierta a un proceso si hay alguno esperando; solo si la cola está vacía suma 1. Por eso el valor no sube cuando alguien estaba dormido.

Pregunta 17Única

En el piso, cada compañero hace: si no hay leche y no hay nota, deja una nota, compra y quita la nota. ¿Qué puede salir mal?

  1. Que los dos miren, vean que no hay nota y compren los dos, porque el cambio de contexto llega antes de dejar la nota
  2. Que nadie compre nunca
  3. Que uno compre dos veces seguidas
  4. Nada: la nota lo resuelve siempre
Solución

Respuesta: Que los dos miren, vean que no hay nota y compren los dos, porque el cambio de contexto llega antes de dejar la nota

Mirar la nota y dejarla son dos pasos separados. Si los dos miran antes de que ninguno la deje, compran los dos: la «solución» funciona casi siempre y falla en el peor momento.

Pregunta 18Única

Ahora cada uno deja SU nota (notaA, notaB) antes de mirar la del otro. ¿Cuál es el peor caso?

  1. Que los dos dejen su nota, cada uno vea la del otro y ninguno compre
  2. Que compren los dos
  3. Que uno espere eternamente en un bucle
  4. Que la leche se compre dos veces por la tarde
Solución

Respuesta: Que los dos dejen su nota, cada uno vea la del otro y ninguno compre

La exclusión mutua queda resuelta, pero si los dos dejan la nota a la vez, los dos se echan atrás y la nevera sigue vacía.

Pregunta 19Verdadero o falso

Solución con una variable turno compartida (while (turno != mi_numero) {}). Verdadero o falso:

  1. Garantiza exclusión mutua entre los dos procesos.
  2. Obliga a que los procesos entren alternándose, quieran o no.
  3. Cumple el requisito de progreso.
  4. Mientras espera, el proceso consume CPU dando vueltas al bucle.
Solución
  • V — Garantiza exclusión mutua entre los dos procesos.
  • V — Obliga a que los procesos entren alternándose, quieran o no.
  • F — Cumple el requisito de progreso.
  • V — Mientras espera, el proceso consume CPU dando vueltas al bucle.

Si al proceso 2 le toca el turno pero no quiere entrar, el 1 no puede volver a entrar aunque la sección esté libre: alternancia estricta, falla el progreso.

Pregunta 20Múltiple · penaliza

Sobre el algoritmo de Peterson para dos procesos:

  1. Cumple exclusión mutua, progreso y espera limitada
  2. Usa dos variables «quiero entrar» y una variable turno, todas compartidas
  3. Un procesador o compilador moderno que reordene lecturas y escrituras puede romperlo
  4. Funciona igual para cualquier número de procesos sin cambios
  5. No consume CPU mientras espera
  6. Necesita inhibir las interrupciones
Solución

Respuesta: Cumple exclusión mutua, progreso y espera limitada · Usa dos variables «quiero entrar» y una variable turno, todas compartidas · Un procesador o compilador moderno que reordene lecturas y escrituras puede romperlo

Peterson combina la nota con nombre (quiere1, quiere2) con la variable turno. Es correcto en teoría, pero solo para dos procesos, con espera activa y sensible a la reordenación de instrucciones.

Pregunta 21Única

Con Peterson, los dos hilos ponen su quiere a true. Después el hilo 1 escribe turno = 2 y justo después el hilo 2 escribe turno = 1. ¿Quién entra primero?

  1. El hilo 1
  2. El hilo 2
  3. Los dos a la vez
  4. Ninguno: se bloquean los dos
Solución

Respuesta: El hilo 1

El hilo 2 espera mientras quiere1 && turno == 1, y las dos cosas son ciertas. El hilo 1 espera mientras quiere2 && turno == 2, y turno vale 1: entra. El último que escribe turno es el que cede.

Pregunta 22Múltiple · penaliza

¿Qué es cierto de un mutex (cerrojo)?

  1. Tiene dos estados, libre y adquirido, y empieza libre
  2. Si un hilo hace lock() y el mutex ya está adquirido, ese hilo se bloquea hasta conseguirlo
  3. Al hacer unlock(), si hay hilos esperando, uno de ellos lo adquiere y se desbloquea
  4. Sus operaciones son lock() y signal()
  5. lock() nunca bloquea al hilo que lo llama
  6. Cada hilo tiene su propio mutex para su sección crítica
Solución

Respuesta: Tiene dos estados, libre y adquirido, y empieza libre · Si un hilo hace lock() y el mutex ya está adquirido, ese hilo se bloquea hasta conseguirlo · Al hacer unlock(), si hay hilos esperando, uno de ellos lo adquiere y se desbloquea

mutex = mutual exclusion. lock() adquiere (o bloquea), unlock() libera (y despierta a uno). Las dos operaciones son atómicas.

Pregunta 23Múltiple · penaliza

Sobre las variables condición:

  1. Siempre van asociadas a un mutex
  2. wait() bloquea al hilo y, mientras duerme, libera el mutex
  3. Si nadie espera, signal() no hace nada
  4. broadcast() despierta a todos los hilos de la cola
  5. signal() sin nadie en la cola se guarda para el próximo wait()
  6. Sustituyen al mutex: con ellas ya no hace falta lock()
Solución

Respuesta: Siempre van asociadas a un mutex · wait() bloquea al hilo y, mientras duerme, libera el mutex · Si nadie espera, signal() no hace nada · broadcast() despierta a todos los hilos de la cola

Una variable condición es una cola de hilos que esperan una condición dentro de una sección crítica. Al despertar, el hilo vuelve a adquirir el mutex antes de seguir.

Pregunta 24Relacionar

P hace signal sobre una condición en la que espera Q. Relaciona:

Estilo Hoare
Estilo Mesa
broadcast
Desbloqueo FIFO

Definiciones:

  • Se despierta el que lleva más tiempo esperando
  • Q se queda dormido hasta el siguiente cambio de contexto
  • Se despiertan todos los que esperan y compiten por el mutex
  • Q pasa a listo, pero espera a que P suelte el mutex
  • Q sigue enseguida y P se queda sin el mutex hasta que Q termine
Solución
  • Estilo Hoare → Q sigue enseguida y P se queda sin el mutex hasta que Q termine
  • Estilo Mesa → Q pasa a listo, pero espera a que P suelte el mutex
  • broadcast → Se despiertan todos los que esperan y compiten por el mutex
  • Desbloqueo FIFO → Se despierta el que lleva más tiempo esperando

El estilo Mesa es el más usado hoy. Cuando hay varios esperando, la elección puede ser FIFO, por prioridades o al azar; broadcast los despierta a todos.

Pregunta 25Única

¿Por qué se escribe while (condicion) cond.wait(); y no if (condicion) cond.wait();?

  1. Porque con el estilo Mesa, cuando el hilo vuelve a tener el mutex la condición puede haber cambiado otra vez, y hay que comprobarla de nuevo
  2. Porque if no se puede usar dentro de una sección crítica
  3. Porque el while hace espera activa, que es más rápida
  4. Da igual: los dos son equivalentes
Solución

Respuesta: Porque con el estilo Mesa, cuando el hilo vuelve a tener el mutex la condición puede haber cambiado otra vez, y hay que comprobarla de nuevo

Entre el signal y el momento en que el hilo despierto recupera el mutex, otro hilo puede entrar y volver a llenar (o vaciar) el búfer. El while vuelve a mirar.

Pregunta 26Rellenar huecos

Herramientas de sincronización. Completa:

Los ____ los propuso Dijkstra en 1965. De los ____ de Hoare (1972) salen el mutex y las variables condición. Los ____ y los buzones no necesitan memoria ____.

Palabras:

  • monitores
  • interrupciones
  • privada
  • registros
  • compartida
  • semáforos
  • canales
Solución

Los semáforos los propuso Dijkstra en 1965. De los monitores de Hoare (1972) salen el mutex y las variables condición. Los canales y los buzones no necesitan memoria compartida.

Con memoria compartida: semáforos, regiones críticas condicionales y monitores. Sin memoria compartida: canales, buzones y promesas y futuros.

Pregunta 27Única

En el problema del búfer finito con N huecos y un contador cola, ¿cuándo debe esperar el productor?

  1. Cuando cola == N
  2. Cuando cola == 0
  3. Cuando cola < N
  4. Nunca: el consumidor ya vaciará el búfer
Solución

Respuesta: Cuando cola == N

Productor: espera si está lleno (cola == N). Consumidor: espera si está vacío (cola == 0).

Pregunta 28Múltiple · penaliza

cola vale 4. Un productor hace cola++ y un consumidor cola-- a la vez, sin protección (cada uno lee, opera y escribe). ¿Con qué valores puede acabar cola?

  1. 4
  2. 5
  3. 3
  4. 6
  5. 2
Solución

Respuesta: 4 · 5 · 3

Si los dos leen 4 antes de que ninguno escriba, gana el último que escribe: 5 o 3. Solo el orden «uno entero y luego el otro» da el 4 correcto.

Pregunta 29Verdadero o falso

Lectores y escritores sobre una base de datos. Verdadero o falso:

  1. Pueden leer varios lectores a la vez.
  2. Un escritor puede escribir mientras hay lectores dentro, si no tocan la misma fila.
  3. Si se da prioridad a los lectores, un escritor puede esperar indefinidamente.
  4. Si se da prioridad a los escritores, desaparece todo riesgo de inanición.
Solución
  • V — Pueden leer varios lectores a la vez.
  • F — Un escritor puede escribir mientras hay lectores dentro, si no tocan la misma fila.
  • V — Si se da prioridad a los lectores, un escritor puede esperar indefinidamente.
  • F — Si se da prioridad a los escritores, desaparece todo riesgo de inanición.

Varios lectores o un solo escritor, nunca mezclados. Prioridad a los lectores → inanición de escritores; prioridad a los escritores → inanición de lectores.

Pregunta 30Múltiple · penaliza

En el problema de los cinco filósofos, ¿qué medidas evitan el interbloqueo?

  1. Que los pares cojan primero un palillo y los impares el otro
  2. Dejar que como mucho cuatro filósofos intenten coger palillos a la vez
  3. Coger los dos palillos en una sola operación atómica
  4. Que todos cojan primero el palillo de su izquierda
  5. Que esperen girando en un while hasta que haya palillo
Solución

Respuesta: Que los pares cojan primero un palillo y los impares el otro · Dejar que como mucho cuatro filósofos intenten coger palillos a la vez · Coger los dos palillos en una sola operación atómica

Si los cinco cogen el palillo izquierdo a la vez, cada uno espera el que tiene el vecino: interbloqueo. Las tres soluciones de los apuntes rompen ese círculo.

Si fallas varias de las soluciones de la leche, repasa qué requisito rompe cada intento. Una nota: exclusión mutua. Notas con nombre: progreso. Variable turno: progreso (alternancia estricta).

Trazas paso a paso: carreras, mutex y semáforos

Aquí se trata de seguir la ejecución instrucción a instrucción, como en las preguntas de cálculo. Las reglas para todas las tablas:

  • Un semáforo nunca baja de 0. wait resta 1 si puede; si vale 0, el proceso se bloquea en la cola.
  • signal despierta a uno si hay alguien en la cola (FIFO) y entonces el valor no cambia; si no hay nadie, suma 1.
  • En los estados escribe una sola palabra: dentro, fuera, bloqueado, imprime o listo, según la tabla. En Peterson, entra o espera. Las tildes y las mayúsculas dan igual.
Pregunta 1Cálculo

El bote de la cena de clase, en una hoja compartida, tiene 200. El hilo 1 añade 30 y el hilo 2 saca 80 para un pago; cada uno lee, opera y escribe, y el planificador alterna un paso de cada uno. Rellena los registros privados (reg1, reg2) y el saldo en memoria después de cada paso:

PasoHiloInstrucciónreg1reg2saldo
11leer–
22leer
31sumar 30
42restar 80
51escribir
62escribir
Solución
PasoHiloInstrucciónreg1reg2saldo
11leer200–200
22leer200200200
31sumar 30230200200
42restar 80230120200
51escribir230120230
62escribir230120120

Los dos hilos leen 200 antes de que nadie escriba. El hilo 1 escribe 230, pero el hilo 2 escribe después su 120 encima: los 30 añadidos se pierden. Lo correcto era 200 + 30 − 80 = 150 y sale 120.

Pregunta 2Cálculo

Tres procesos A, B y C protegen su sección crítica con un semáforo m que empieza en 1 (un mutex). Rellena el valor de m y el estado de cada proceso (dentro, bloqueado o fuera) después de cada operación. La cola es FIFO:

PasoOperaciónmABC
1A: wait(m)
2B: wait(m)
3C: wait(m)
4A: signal(m)
5B: signal(m)
6C: signal(m)
Solución
PasoOperaciónmABC
1A: wait(m)0dentrofuerafuera
2B: wait(m)0dentrobloqueadofuera
3C: wait(m)0dentrobloqueadobloqueado
4A: signal(m)0fueradentrobloqueado
5B: signal(m)0fuerafueradentro
6C: signal(m)1fuerafuerafuera

m baja a 0 cuando entra A, y B y C se ponen en cola. Cuando A hace signal, m NO sube: el permiso pasa directamente a B (el primero de la cola). Lo mismo con B → C. Solo el último signal, sin nadie esperando, devuelve m a 1.

Pregunta 3Cálculo

En el laboratorio hay 2 impresoras y un semáforo impresoras = 2. Cuatro procesos piden impresora con wait y la devuelven con signal. Rellena el valor del semáforo y el estado de cada proceso (imprime, bloqueado o fuera). La cola es FIFO:

PasoOperaciónimpresorasABCD
1A: wait
2B: wait
3C: wait
4B: signal
5D: wait
6A: signal
7C: signal
8D: signal
Solución
PasoOperaciónimpresorasABCD
1A: wait1imprimefuerafuerafuera
2B: wait0imprimeimprimefuerafuera
3C: wait0imprimeimprimebloqueadofuera
4B: signal0imprimefueraimprimefuera
5D: wait0imprimefueraimprimebloqueado
6A: signal0fuerafueraimprimeimprime
7C: signal1fuerafuerafueraimprime
8D: signal2fuerafuerafuerafuera

Un semáforo contador cuenta impresoras libres. A y B se llevan las dos (2 → 0); C y después D se bloquean. Cada signal con alguien esperando le pasa la impresora sin tocar el valor; al final, sin nadie esperando, vuelve a subir a 1 y a 2.

Pregunta 4Cálculo

Spotify con un búfer de 2 huecos: semáforos huecos = 2 y llenos = 0. D (descargador) mete un trozo y R (reproductor) saca uno; quien es despertado por un signal ya tiene su permiso y completa su operación en su siguiente turno. Rellena, después de cada turno, los trozos del búfer, huecos, llenos y el estado de D y de R (listo o bloqueado):

TurnoQuiénAcciónTrozoshuecosllenosDR
1Dmete
2Dmete
3Dbloquea
4Rsaca
5Rsaca
6Rbloquea
7Dmete
Solución
TurnoQuiénAcciónTrozoshuecosllenosDR
1Dmete111listolisto
2Dmete202listolisto
3Dbloquea202bloqueadolisto
4Rsaca101listolisto
5Rsaca010listolisto
6Rbloquea010listobloqueado
7Dmete110listolisto

Turno 3: no quedan huecos y D se bloquea. Turno 4: R saca un trozo y su signal(huecos) despierta a D, así que huecos sigue a 0. Turno 6: el búfer está vacío y R se bloquea. Turno 7: D mete su trozo sin esperar (ya tenía el permiso) y su signal(llenos) despierta a R, así que llenos sigue a 0.

Pregunta 5Cálculo

Algoritmo de Peterson, pero esta vez empieza el hilo 2. Rellena quiere1, quiere2 y turno después de cada instrucción y, en las filas del while, si el hilo entra o espera (entra / espera). Al principio quiere1 = quiere2 = false y turno = 1:

PasoHiloInstrucciónquiere1quiere2turnoResultado
12quiere2 = true–
22turno = 1–
32while (quiere1 && turno == 1)
41quiere1 = true–
51turno = 2–
61while (quiere2 && turno == 2)
72quiere2 = false–
81while (quiere2 && turno == 2)
Solución
PasoHiloInstrucciónquiere1quiere2turnoResultado
12quiere2 = truefalsetrue1–
22turno = 1falsetrue1–
32while (quiere1 && turno == 1)falsetrue1entra
41quiere1 = truetruetrue1–
51turno = 2truetrue2–
61while (quiere2 && turno == 2)truetrue2espera
72quiere2 = falsetruefalse2–
81while (quiere2 && turno == 2)truefalse2entra

El hilo 2 encuentra quiere1 = false y entra directamente. El hilo 1 levanta la mano, cede (turno = 2) y espera, porque el hilo 2 quiere y es su turno. En cuanto el hilo 2 pone quiere2 = false, la condición del hilo 1 deja de cumplirse y entra.

Subir el semáforo en un signal cuando había alguien bloqueado. Si hay cola, el permiso pasa directamente al despertado y el valor se queda igual: por eso en la traza del mutex m vale 0 durante cinco pasos seguidos.

Preguntas de desarrollo

Cuatro preguntas largas como las del examen. Escribe tu respuesta en la caja y después compárala con la respuesta modelo. No hay nota automática: tú decides si has cubierto cada punto de «lo que se valora».

Pregunta 1Desarrollo

¿Qué es una condición de carrera? Explícalo con dos hilos que incrementan el mismo contador e indica cómo se evita.

Solución

Respuesta modelo:

Una condición de carrera ocurre cuando dos o más hilos acceden a un dato compartido, al menos uno lo modifica y el resultado depende del orden en que se intercalen sus instrucciones.

Ejemplo: contador vale 7 y dos hilos hacen contador = contador + 1. En la CPU son tres pasos: leer a un registro, sumar y escribir. Si el hilo 1 lee 7 y le quitan la CPU antes de escribir, el hilo 2 también lee 7; los dos calculan 8 y los dos escriben 8. Se pierde una suma: debería valer 9.

Se evita convirtiendo el acceso al dato en una sección crítica con exclusión mutua: por ejemplo, rodeando la suma con mutex.lock() y mutex.unlock(), o con una instrucción atómica. Así, el segundo hilo no puede leer hasta que el primero haya escrito.

Lo que se valora:

  • Definición: dato compartido, alguna escritura y resultado que depende del intercalado
  • El incremento son tres pasos (leer, sumar, escribir) y no es atómico
  • Un intercalado concreto que pierde una actualización, con los valores
  • La solución: sección crítica con exclusión mutua (mutex, semáforo o instrucción atómica)

Lo importante es el porqué: el hilo trabaja con una copia en su registro y escribe un valor calculado a partir de un dato que ya está viejo.

Pregunta 2Desarrollo

Enumera y explica los tres requisitos de una solución al problema de la sección crítica. Después, razona si inhibir las interrupciones es una buena solución.

Solución

Respuesta modelo:

1. Exclusión mutua: nunca puede haber más de un proceso o hilo ejecutando su sección crítica.

2. Progreso: si nadie está en la sección crítica y hay procesos que quieren entrar, solo esos procesos participan en la decisión y esta se toma en un tiempo finito.

3. Espera limitada: existe un límite al número de veces que otros procesos pueden entrar antes que uno que ya ha pedido entrar.

Inhibir las interrupciones consigue exclusión mutua en un único procesador, porque sin interrupciones nadie expulsa al proceso. Pero no es buena solución general: suspende toda la concurrencia del sistema (perjudica a los que no quieren entrar), no sirve en multiprocesadores (cada CPU tiene sus interrupciones y otra puede entrar) y no garantiza espera limitada.

Lo que se valora:

  • Los tres requisitos con su nombre exacto
  • Una definición correcta de cada uno
  • Por qué inhibir interrupciones da exclusión mutua en un procesador
  • Sus tres desventajas: congela el sistema, no vale en multiprocesadores, no garantiza espera limitada

Un examen suele pedir el nombre exacto de los requisitos: exclusión mutua, progreso y espera limitada.

Pregunta 3Desarrollo

Compara un mutex y una variable condición: para qué sirve cada uno, qué operaciones tienen y qué pasa tras un signal en el estilo Hoare y en el estilo Mesa.

Solución

Respuesta modelo:

Un mutex sirve para tener acceso exclusivo a un recurso. Tiene dos estados (libre y adquirido) y dos operaciones atómicas: lock() lo adquiere o bloquea al proceso si ya estaba adquirido; unlock() lo libera y, si hay procesos esperando, uno de ellos lo adquiere.

Una variable condición sirve para esperar a que se cumpla una condición dentro de una sección crítica. Siempre va asociada a un mutex y gestiona una cola de procesos. Operaciones: wait() bloquea al proceso y libera el mutex mientras duerme (al despertar debe volver a adquirirlo); signal() despierta a uno de la cola (si no hay nadie, no hace nada); broadcast() despierta a todos.

Tras un signal de P con Q esperando: en el estilo Hoare, Q se reanuda inmediatamente y P pierde el mutex; en el estilo Mesa, Q se desbloquea pero espera a que P libere el mutex. Mesa es el más usado, y por eso la condición se comprueba con while y no con if.

Lo que se valora:

  • Para qué sirve cada herramienta (exclusión frente a espera por una condición)
  • Operaciones del mutex: lock y unlock, con su efecto
  • Operaciones de la variable condición: wait (libera el mutex), signal y broadcast
  • Hoare frente a Mesa y la consecuencia del while

La pista para no mezclarlas: el mutex responde a «¿puedo entrar?»; la variable condición, a «¿ya se cumple lo que espero?».

Pregunta 4Desarrollo

Problema del búfer finito: explica qué condiciones hay que respetar y cómo se resuelve con un mutex y dos variables condición (escribe el pseudocódigo del productor y del consumidor).

Solución

Respuesta modelo:

Productores y consumidores comparten una cola FIFO de N posiciones. El productor no debe meter si el búfer está lleno (cola == N) y el consumidor no debe sacar si está vacío (cola == 0). Además, cola++ y cola– tocan un dato compartido: sin protección, un intercalado puede dejar un valor incorrecto.

Productor: mutex.lock(); while (cola == N) cprod.wait(); buffer[in] = cosa; in = (in + 1) % N; cola++; ccons.signal(); mutex.unlock();

Consumidor: mutex.lock(); while (cola == 0) ccons.wait(); cosa = buffer[out]; out = (out + 1) % N; cola--; cprod.signal(); mutex.unlock(); consumir(cosa);

El mutex garantiza que solo uno toca buffer, in, out y cola. Cada uno espera en su variable condición y avisa al otro con signal después de cambiar el estado. Se usa while porque, con el estilo Mesa, al despertar hay que volver a comprobar la condición.

Lo que se valora:

  • Las dos condiciones: lleno (cola == N) y vacío (cola == 0)
  • El mutex rodea todo el acceso a buffer, índices y cola
  • Cada lado espera en su condición y señala la del otro
  • while y no if, por el estilo Mesa
  • consumir() fuera de la sección crítica

consumir(cosa) va después de unlock(): no toca datos compartidos y, si estuviera dentro, haría esperar al productor sin motivo.

Todo el tema en un dibujo

Si solo puedes repasar una cosa antes del examen, que sea este mapa: el problema (datos compartidos y condición de carrera), la regla (sección crítica y sus tres requisitos) y las tres familias de soluciones.

Mapa del tema de concurrencia Concurrencia y datos compartidos llevan a condiciones de carrera; la sección crítica y sus tres requisitos; soluciones software, hardware y del sistema operativo; problemas clásicos. Todo el tema en un dibujoConcurrenciaDatos compartidosCondición de carreraSección crítica → exclusión mutua + progreso + espera limitadaentrada → SC → salidaSoftwarenota / nota con nombrevariable turnoPetersonHardwareinhibir interrupcionestest-and-setcompare-and-swapSistema operativomutex: lock / unlockvar. condición: wait/signalsemáforos (Dijkstra)Clásicos: búfer finito · lectores y escritores · filósofos (interbloqueo)
De izquierda a derecha y de arriba abajo: el problema, la regla y las tres familias de soluciones.

Chuletario: concurrencia y sincronización

Lo esencial en 30 segundos

  • Concurrencia = varios procesos existen a la vez. Paralelismo = se ejecutan a la vez en varios procesadores. Distribuida = paralelismo en red.
  • x = x + 1 son tres pasos (leer, sumar, escribir): un cambio de contexto en medio provoca una condición de carrera.
  • Sección crítica = código que toca datos compartidos. Requisitos: exclusión mutua, progreso y espera limitada.
  • Hardware: inhibir interrupciones (solo un procesador), test-and-set y compare-and-swap (espera activa, sin espera limitada).
  • Sistema operativo: mutex (lock/unlock), variables condición (wait/signal/broadcast, siempre con un mutex) y semáforos (contador + cola).
  • Clásicos: búfer finito, lectores y escritores, filósofos (interbloqueo).

Si te preguntan… / Respuesta

Si te preguntan…Respuesta
¿Concurrencia implica ejecución simultánea?No: eso es paralelismo
¿Cómo se expresa la concurrencia en el código?API (pthreads), objetos concurrentes (Java, C++), sentencias concurrentes (Ada, Go)
¿Para qué sirve la programación concurrente?Expresar el mundo real, aprovechar varios procesadores, facilitar algunos algoritmos
¿Qué falla con una nota?Exclusión mutua: compran los dos
¿Qué falla con notas con nombre?Progreso: puede que nadie compre
¿Qué falla con la variable turno?Progreso: alternancia estricta
¿Qué cumple Peterson?Los tres requisitos (pega: reordenación, dos procesos, espera activa)
¿Inhibir interrupciones vale en multiprocesador?No, y tampoco garantiza espera limitada
¿Qué devuelve test-and-set?El valor antiguo, tras ponerlo a true
¿Qué devuelve CAS(M, A, B)?Cierto si M = A (y entonces M ← B); falso si no
¿Qué hace wait() en una variable condición?Bloquea y libera el mutex mientras duerme
¿signal() sin nadie en la cola?Variable condición: nada. Semáforo: suma 1
¿Hoare o Mesa?Hoare: Q sigue ya. Mesa: Q espera a que P suelte el mutex (el más usado)
¿Herramientas sin memoria compartida?Canales, buzones, promesas y futuros
¿Prioridad a los lectores?Inanición de los escritores (y al revés)
¿Soluciones de los filósofos?Asimétrico pares/impares, máximo cuatro pidiendo, coger los dos palillos atómicamente

Plantillas

Sección crítica con mutex:

mutex.lock();
// sección crítica
mutex.unlock();

Esperar una condición (estilo Mesa, por eso while):

mutex.lock();
while (!condicion) cond.wait();   // duerme y suelta el mutex
// … ya se cumple
mutex.unlock();

Búfer finito con semáforos (huecos = N, llenos = 0, m = 1):

// Productor                     // Consumidor
wait(huecos);                    wait(llenos);
wait(m);                         wait(m);
meter(cosa);                     cosa = sacar();
signal(m);                       signal(m);
signal(llenos);                  signal(huecos);

Peterson (proceso 1; el 2 es simétrico):

quiere1 = true;
turno = 2;
while (quiere2 && turno == 2) { }
// sección crítica
quiere1 = false;

Síntoma / Causa probable

SíntomaCausa probable
El contador acaba con menos de lo esperadoCondición de carrera: leer-sumar-escribir sin protección
Dos procesos dentro de la sección críticaFalla la exclusión mutua (nota simple, interrupciones en multiprocesador)
Sección libre y nadie entraFalla el progreso (notas con nombre, variable turno)
Uno nunca consigue entrar aunque el resto avanzaInanición: falta espera limitada o hay prioridad para otro grupo
Nadie avanza y todos esperan a otroInterbloqueo (filósofos con el palillo izquierdo)
La CPU al 100 % mientras un hilo esperaEspera activa (while que comprueba una y otra vez)
Un hilo despierta y el búfer sigue vacíoSe usó if en lugar de while con el estilo Mesa

Antes de entregar, pregúntate

  1. ¿He distinguido concurrencia (existir a la vez) de paralelismo (ejecutarse a la vez)?
  2. ¿He escrito los tres requisitos con su nombre exacto: exclusión mutua, progreso y espera limitada?
  3. En las trazas, ¿he comprobado si había alguien en la cola antes de sumar en un signal?
  4. ¿Sé decir qué requisito rompe cada solución y cuál es la pega de las que los cumplen todos?

Con esto termina el bloque de procesos de IC2. Si quieres repasar, vuelve a la teoría de concurrencia y sincronización o a los ejercicios de planificación de la CPU. El siguiente tema es la gestión de memoria: memoria contigua, segmentación y paginación.

Sigue aprendiendoTodos los artículos de Sistemas operativos

¿Te ha servido este artículo? Así puedes apoyar SergioLearns →

¿Listo para el examen?Simulacro del examen de teoría de IC2 — primer parcial, segundo parcial o convocatoria, con tu nota real y el repaso de cada fallo (como si estuvieras en el aula)