Diferencia entre revisiones de «Concurrencia de procesos»

De Wiki de Sistemas Operativos
Saltar a: navegación, buscar
(Formas de interacción entre procesos)
 
(No se muestran 35 ediciones intermedias de 16 usuarios)
Línea 1: Línea 1:
 
La concurrencia de procesos se refiere a las situaciones en las que dos o más procesos puedan coincidir en el acceso a un recurso compartido o, dicho de otra forma, que requieran coordinarse en su ejecución. Para evitar dicha coincidencia, el sistema operativo ofrece mecanismos de arbitraje que permiten coordinar la ejecución de los procesos.
 
La concurrencia de procesos se refiere a las situaciones en las que dos o más procesos puedan coincidir en el acceso a un recurso compartido o, dicho de otra forma, que requieran coordinarse en su ejecución. Para evitar dicha coincidencia, el sistema operativo ofrece mecanismos de arbitraje que permiten coordinar la ejecución de los procesos.
  
Un ejemplo de un problema de concurrencia sería el siguiente: Dados dos procesos A y B, suponiendo que ambos se ejecutan indefinidamente en el tiempo, el proceso A debe recibir tiempo de ejecución antes que B, tras esto, el proceso B debe recibir su oportunidad de ejecución, dando paso de nuevo al proceso A y así sucesivamente.
+
= El problema del productor y consumidor =
  
proceso A |--  --  --
+
Un ejemplo de un problema de concurrencia sería el siguiente: dados dos procesos A (productor) y B (consumidor) que se ejecutan indefinidamente en el tiempo, el proceso A debe recibir tiempo de ejecución antes que B. Tras esto, el proceso B debe recibir su oportunidad de ejecución, dando paso de nuevo al proceso A y así sucesivamente, siguiendo un esquema de alternancia estricta.
          |
 
proceso B |  --  --  --
 
          |_____________
 
  
 +
proceso A  |--  --    --
 +
                      |
 +
proceso B  |  --    --    --
 +
                      |_____________
  
Recuerde que el planificador de procesos, al desconocer la naturaleza de los procesos y sus objetivos, no dispone de información suficiente como para garantizar la secuencia de ejecución descrita en el ejemplo anterior. Por tanto, suponiendo que ambos procesos se encuentran en estado preparado, podría seleccionar al proceso B para pasar a estado activo antes de seleccionar al proceso A, situación que no es deseada.
 
  
Recuerde que el planificador de procesos, como hemos visto en capítulos anteriores, emplea [[Criterios de planificación|criterios de planificación]] que no tienen en consideración el objetivo de los procesos. Podemos decir que el planificador de procesos '''desconoce''' cuál es el propósito de los procesos, únicamente puede observar su comportamiento, es decir, si presentan un comportamiento más o menos interactivo.
+
Recuerde que el planificador de procesos, al desconocer la naturaleza de los procesos y sus objetivos, no dispone de información suficiente para garantizar la secuencia de ejecución descrita en el ejemplo anterior. De este modo, suponiendo que ambos procesos se encuentran en estado preparado, podría seleccionar al proceso B para pasar a estado activo antes de seleccionar al proceso A, situación que no es deseada.
  
Por tanto, el programador, a la hora de modelar los procesos, debe emplear mecanismos de arbitraje que ofrece el sistema operativo y que permiten resolver el problema de concurrencia que se plantee.
+
El planificador de procesos, como hemos visto en capítulos anteriores, emplea [[Criterios de planificación|criterios de planificación]] que no tienen en consideración el objetivo de los procesos. Podemos decir que este '''desconoce''' cuál es el propósito de los procesos; únicamente puede observar si presentan un comportamiento más o menos interactivo.
  
= Formas de interación entre procesos =
+
Por tanto, el programador, a la hora de modelar los procesos, debe emplear los mecanismos de arbitraje que ofrece el sistema operativo y que permitirían resolver el problema de concurrencia que se plantee.
 
 
Hay dos formas básicas de interacción entre los procesos:
 
 
 
* '''Relaciones de competencia''': Dados dos procesos A y B, ambos procesos tienen objetivos diferentes y compiten por los recursos para conseguirlos. Por ejemplo, dos procesos desean modificar el contenido de un mismo fichero, pero no deben hacerlo de manera simultánea o quedará en un estado incoherente.
 
 
 
* '''Relaciones de cooperación''': Dados dos procesos A y B, ambos con un mismo objetivo, ''se ponen de acuerdo'' a la hora de emplear los recursos existentes para alcanzarlos. Por ejemplo, el proceso A produce un objeto que es consumido por el proceso B, hasta que el proceso A no produzca, el proceso B no debe consumir y viceversa.
 
  
 
= Mecanismos de arbitraje =
 
= Mecanismos de arbitraje =
  
Los mecanismos de arbitraje que ofrece el sistema operativo son básicamente dos:
+
Los mecanismos de arbitraje que ofrece el sistema operativo son dos principalmente:
  
 
* Mecanismos de '''sincronización''': el sistema operativo ofrece mecanismos que permiten a los procesos coordinar su ejecución para conseguir el objetivo sin que sucedan situaciones no deseadas, como por ejemplo que dos o más procesos coincidan simultáneamente en el acceso a un cierto recurso que no se puede compartir.
 
* Mecanismos de '''sincronización''': el sistema operativo ofrece mecanismos que permiten a los procesos coordinar su ejecución para conseguir el objetivo sin que sucedan situaciones no deseadas, como por ejemplo que dos o más procesos coincidan simultáneamente en el acceso a un cierto recurso que no se puede compartir.
Línea 33: Línea 27:
 
= Programación concurrente =  
 
= Programación concurrente =  
  
La programación concurrente se trata del conjunto de técnicas que nos permiten elaborar programas que resuelven situaciones de concurrencia. En programación concurrente, el programador emplea alguno de los mecanismos de arbitraje que ofrece el sistema operativo, ya sean los de sincronización o mensajería, para resolver el problema de concurrencia.
+
El término programación concurrente se emplea con frecuencia para referirse a un conjunto de programas que funcionan en cooperación.
 +
 
 +
Hay tres formas de interacción entre procesos cooperativos:
 +
 
 +
*Concurrencia: Hay un recurso común, si varios procesos modificaran  la misma información a la vez, cada uno podría destruir parte del trabajo de los demás. Si lo hacen uno tras otro, en serie, se obtendrá el resultado correcto.
 +
 
 +
*Sincronización: El Sistema Operativo se encarga de enviarle señales a los procesos para coordinar su evolución y conseguir que progrese armónicamente.
 +
 
 +
*Comunicación:  El S.O. transmite mensajes entre los procesos, que se usan para intercambiar, enviar o recibir información.
 +
 
 +
Se le llama programación concurrente ya que la concurrencia fue la primera forma de iterar recursos entre procesos cooperativos.
 +
 
 +
= Sección crítica =
 +
 
 +
Una ''sección crítica'' se trata de una sección del código que no debe ser ejecutada por varios procesos o hilos simultáneamente. Un ejemplo de ''sección crítica'' es la sección de código en la que se accede a un recurso compartido. Para evitar el acceso simultaneo a la sección crítica se emplean mecanismos que garantizan la ''exclusión mutua''.
 +
 
 +
La exclusión mutua se debe realizar de forma coordinada y eficiente, para ello se requiere:
 +
 
 +
*No más de un proceso por sección crítica y recurso compartido.
 +
 
 +
*El mismo proceso no puede usar el mismo recurso compartido indefinidamente.
 +
 
 +
*Todo proceso debe entrar antes o después a usar el recurso.
 +
 
 +
*Si el recurso está sin uso, cualquiera que lo requiera dispondrá de él inmediatamente.
 +
 
 +
*Si hay varios esperando usar el recurso y éste se libera, uno de los que estaba esperando lo usará durante un tiempo determinado.
 +
 
 +
Para llevar esto a cabo se necesita un protocolo que indique cuando el recurso está libre y cuando está siendo ocupado.
  
 
= Tipos de mecanismos de sincronización =
 
= Tipos de mecanismos de sincronización =
Línea 39: Línea 61:
 
Los mecanismos de sincronización los podemos catalogar en dos categorías:
 
Los mecanismos de sincronización los podemos catalogar en dos categorías:
  
*[[Mecanismos de sincronización#Optimista|Optimistas]]: Este mecanismo considera que la frecuencia de acceso a un cierto recurso compartido es ''baja''.
+
*[[Mecanismos de sincronización#Optimista|Optimistas]]: Este mecanismo considera que la frecuencia de acceso a un cierto recurso compartido es ''baja''. Este tipo tiene más consumo de memoria, ya que tiene que copiar el recurso compartido y, en caso de interferencia en el hilo, tiene que volver a ejecutarlo y consume más recursos.
*[[Mecanismos de sincronización#Pesimista|Pesimistas]]: Este mecanismo permite coordinar la ejecución de dos o más procesos que acceden al recurso compartida con una frecuencia ''alta''.
+
 
 +
*[[Mecanismos de sincronización#Pesimista|Pesimistas]]: Este mecanismo permite coordinar la ejecución de dos o más procesos que acceden al recurso compartido con una frecuencia ''alta''.
 +
 
 +
Dependerá del criterio del programador el tipo de mecanismo que utilice ya que puede que no elija el tipo correcto. Por ejemplo, puede que la frecuencia de acceso a un cierto recurso sea alta y el programador le asigna un mecanismo optimista, esto provocaría no obtener el resultado esperado.
 +
 
 +
 
 +
== ¿Cómo provocar errores de concurrencia en Java? ==
 +
Pequeño experimento, por si queremos observar cómo se producen errores de concurrencia en operaciones tan simples como la actualización de una lista.
 +
 
 +
<source lang="java">
 +
public static void main(String[] args) {
 +
 
 +
List<Integer> list = new ArrayList<Integer>();
 +
 
 +
/*
 +
* EXPLICACIÓN:
 +
*
 +
* Tenemos creada una lista global, "list" , que será accedida al mismo
 +
* tiempo por varios hilos. En este código se observa como a veces se
 +
* producen errores de concurrencia al realizar modificaciones sobre la
 +
* lista al mismo tiempo. Este es el motivo por el que debemos usar
 +
* mecanismos como los semáforos u otras técnicas para prevenir este
 +
* tipo de errores.
 +
*
 +
* Los errores de concurrencia en java se muestran como:
 +
* ConcurrentModificationError
 +
*
 +
* También es curioso notar que los errores se dan sobretodo al principio, en
 +
* los 30 primeros segundos, luego hay hilos que no se vuelven a
 +
* ejecutar mientras que un par de ellos de forma aleatoria, se ejecutan
 +
* cíclicamente, uno tras el otro.
 +
*
 +
*/
 +
// -----------------------------------------------------------------------------------------------------------------------
 +
 
 +
// Los hilos con esta tarea añaden un número aleatorio de 0 a 100 en la
 +
// lista cada seg.
 +
Runnable tarea = () -> {
 +
while (true) {
 +
String nombreHilo = Thread.currentThread().getName();
 +
System.out.println("Hola soy el hilo " + nombreHilo);
 +
list.add(new Random().nextInt(100));
 +
System.out.println(list);
 +
try {
 +
Thread.sleep(1000);
 +
} catch (InterruptedException e) {
 +
 
 +
e.printStackTrace();
 +
}
 +
}
 +
};
 +
 
 +
// Los hilos con esta tarea añaden también otro número aleatorio de 0 a
 +
// 100 en la lista cada seg.
 +
 
 +
Runnable tarea2 = () -> {
 +
while (true) {
 +
String nombreHilo = Thread.currentThread().getName();
 +
System.out.println("Hola soy el hilo " + nombreHilo);
 +
list.add(new Random().nextInt(100));
 +
System.out.println(list);
 +
try {
 +
Thread.sleep(1000);
 +
} catch (InterruptedException e) {
 +
 
 +
e.printStackTrace();
 +
}
 +
}
 +
};
 +
 
 +
// Los hilos con esta tarea eliminan un número de una posición aleatoria
 +
// cada seg.
 +
Runnable tarea3 = () -> {
 +
while (true) {
 +
String nombreHilo = Thread.currentThread().getName();
 +
System.out.println("Hola soy el hilo " + nombreHilo);
 +
Random r = new Random(list.size());
 +
if (list.contains(r))
 +
list.remove(r);
 +
System.out.println(list);
 +
try {
 +
Thread.sleep(1000);
 +
} catch (InterruptedException e) {
 +
 
 +
e.printStackTrace();
 +
}
 +
}
 +
};
 +
 
 +
//Creación de Hilos.
 +
Thread hilo = new Thread(tarea);
 +
Thread hilo2 = new Thread(tarea2);
 +
Thread hilo3 = new Thread(tarea3);
 +
Thread hilo4 = new Thread(tarea3);
 +
Thread hilo5 = new Thread(tarea);
 +
Thread hilo6 = new Thread(tarea3);
 +
 
 +
//Ejecución.
 +
hilo.start();
 +
hilo2.start();
 +
hilo3.start();
 +
hilo4.start();
 +
hilo5.start();
 +
hilo6.start();
 +
 
 +
}
 +
 
 +
</source>
 +
 
 +
 
 +
 
 +
5.2[[Mecanismos_de_sincronización | Control optimista y pesimista de la concurrencia]]

Revisión actual del 17:28 2 abr 2020

La concurrencia de procesos se refiere a las situaciones en las que dos o más procesos puedan coincidir en el acceso a un recurso compartido o, dicho de otra forma, que requieran coordinarse en su ejecución. Para evitar dicha coincidencia, el sistema operativo ofrece mecanismos de arbitraje que permiten coordinar la ejecución de los procesos.

El problema del productor y consumidor

Un ejemplo de un problema de concurrencia sería el siguiente: dados dos procesos A (productor) y B (consumidor) que se ejecutan indefinidamente en el tiempo, el proceso A debe recibir tiempo de ejecución antes que B. Tras esto, el proceso B debe recibir su oportunidad de ejecución, dando paso de nuevo al proceso A y así sucesivamente, siguiendo un esquema de alternancia estricta.

proceso A  |--   --    --
                      |
proceso B  |   --    --     --
                      |_____________


Recuerde que el planificador de procesos, al desconocer la naturaleza de los procesos y sus objetivos, no dispone de información suficiente para garantizar la secuencia de ejecución descrita en el ejemplo anterior. De este modo, suponiendo que ambos procesos se encuentran en estado preparado, podría seleccionar al proceso B para pasar a estado activo antes de seleccionar al proceso A, situación que no es deseada.

El planificador de procesos, como hemos visto en capítulos anteriores, emplea criterios de planificación que no tienen en consideración el objetivo de los procesos. Podemos decir que este desconoce cuál es el propósito de los procesos; únicamente puede observar si presentan un comportamiento más o menos interactivo.

Por tanto, el programador, a la hora de modelar los procesos, debe emplear los mecanismos de arbitraje que ofrece el sistema operativo y que permitirían resolver el problema de concurrencia que se plantee.

Mecanismos de arbitraje

Los mecanismos de arbitraje que ofrece el sistema operativo son dos principalmente:

  • Mecanismos de sincronización: el sistema operativo ofrece mecanismos que permiten a los procesos coordinar su ejecución para conseguir el objetivo sin que sucedan situaciones no deseadas, como por ejemplo que dos o más procesos coincidan simultáneamente en el acceso a un cierto recurso que no se puede compartir.
  • Mecanismos de mensajería: el sistema operativo ofrece mecanismos de comunicación entre procesos mediante mensajes. El intercambio de mensajes entre procesos permite coordinarlos.

Programación concurrente

El término programación concurrente se emplea con frecuencia para referirse a un conjunto de programas que funcionan en cooperación.

Hay tres formas de interacción entre procesos cooperativos:

  • Concurrencia: Hay un recurso común, si varios procesos modificaran la misma información a la vez, cada uno podría destruir parte del trabajo de los demás. Si lo hacen uno tras otro, en serie, se obtendrá el resultado correcto.
  • Sincronización: El Sistema Operativo se encarga de enviarle señales a los procesos para coordinar su evolución y conseguir que progrese armónicamente.
  • Comunicación: El S.O. transmite mensajes entre los procesos, que se usan para intercambiar, enviar o recibir información.

Se le llama programación concurrente ya que la concurrencia fue la primera forma de iterar recursos entre procesos cooperativos.

Sección crítica

Una sección crítica se trata de una sección del código que no debe ser ejecutada por varios procesos o hilos simultáneamente. Un ejemplo de sección crítica es la sección de código en la que se accede a un recurso compartido. Para evitar el acceso simultaneo a la sección crítica se emplean mecanismos que garantizan la exclusión mutua.

La exclusión mutua se debe realizar de forma coordinada y eficiente, para ello se requiere:

  • No más de un proceso por sección crítica y recurso compartido.
  • El mismo proceso no puede usar el mismo recurso compartido indefinidamente.
  • Todo proceso debe entrar antes o después a usar el recurso.
  • Si el recurso está sin uso, cualquiera que lo requiera dispondrá de él inmediatamente.
  • Si hay varios esperando usar el recurso y éste se libera, uno de los que estaba esperando lo usará durante un tiempo determinado.

Para llevar esto a cabo se necesita un protocolo que indique cuando el recurso está libre y cuando está siendo ocupado.

Tipos de mecanismos de sincronización

Los mecanismos de sincronización los podemos catalogar en dos categorías:

  • Optimistas: Este mecanismo considera que la frecuencia de acceso a un cierto recurso compartido es baja. Este tipo tiene más consumo de memoria, ya que tiene que copiar el recurso compartido y, en caso de interferencia en el hilo, tiene que volver a ejecutarlo y consume más recursos.
  • Pesimistas: Este mecanismo permite coordinar la ejecución de dos o más procesos que acceden al recurso compartido con una frecuencia alta.

Dependerá del criterio del programador el tipo de mecanismo que utilice ya que puede que no elija el tipo correcto. Por ejemplo, puede que la frecuencia de acceso a un cierto recurso sea alta y el programador le asigna un mecanismo optimista, esto provocaría no obtener el resultado esperado.


¿Cómo provocar errores de concurrencia en Java?

Pequeño experimento, por si queremos observar cómo se producen errores de concurrencia en operaciones tan simples como la actualización de una lista.

public static void main(String[] args) {

		List<Integer> list = new ArrayList<Integer>();

		/*
		 * EXPLICACIÓN:
		 * 
		 * Tenemos creada una lista global, "list" , que será accedida al mismo
		 * tiempo por varios hilos. En este código se observa como a veces se
		 * producen errores de concurrencia al realizar modificaciones sobre la
		 * lista al mismo tiempo. Este es el motivo por el que debemos usar
		 * mecanismos como los semáforos u otras técnicas para prevenir este
		 * tipo de errores.
		 * 
		 * Los errores de concurrencia en java se muestran como:
		 * ConcurrentModificationError
		 *
		 * También es curioso notar que los errores se dan sobretodo al principio, en
		 * los 30 primeros segundos, luego hay hilos que no se vuelven a
		 * ejecutar mientras que un par de ellos de forma aleatoria, se ejecutan
		 * cíclicamente, uno tras el otro.
		 * 
		 */
		// -----------------------------------------------------------------------------------------------------------------------

		// Los hilos con esta tarea añaden un número aleatorio de 0 a 100 en la
		// lista cada seg.
		Runnable tarea = () -> {
			while (true) {
				String nombreHilo = Thread.currentThread().getName();
				System.out.println("Hola soy el hilo " + nombreHilo);
				list.add(new Random().nextInt(100));
				System.out.println(list);
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {

					e.printStackTrace();
				}
			}
		};

		// Los hilos con esta tarea añaden también otro número aleatorio de 0 a
		// 100 en la lista cada seg.

		Runnable tarea2 = () -> {
			while (true) {
				String nombreHilo = Thread.currentThread().getName();
				System.out.println("Hola soy el hilo " + nombreHilo);
				list.add(new Random().nextInt(100));
				System.out.println(list);
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {

					e.printStackTrace();
				}
			}
		};

		// Los hilos con esta tarea eliminan un número de una posición aleatoria
		// cada seg.
		Runnable tarea3 = () -> {
			while (true) {
				String nombreHilo = Thread.currentThread().getName();
				System.out.println("Hola soy el hilo " + nombreHilo);
				Random r = new Random(list.size());
				if (list.contains(r))
					list.remove(r);
				System.out.println(list);
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {

					e.printStackTrace();
				}
			}
		};

		//Creación de Hilos.
		Thread hilo = new Thread(tarea);
		Thread hilo2 = new Thread(tarea2);
		Thread hilo3 = new Thread(tarea3);
		Thread hilo4 = new Thread(tarea3);
		Thread hilo5 = new Thread(tarea);
		Thread hilo6 = new Thread(tarea3);

		//Ejecución.
		hilo.start();
		hilo2.start();
		hilo3.start();
		hilo4.start();
		hilo5.start();
		hilo6.start();

	}


5.2 Control optimista y pesimista de la concurrencia