Bienvenidos sean a este post, hoy hablaremos sobre una forma de trabajo del CPU.
Todos sabemos que la forma mas simple de correr un programa es ejecutando sus instrucciones una tras otra por el CPU. Si vienen de posts anteriores, sabran que los programas contienen muchas secciones y una de ellas son las instrucciones del programa. Donde cada instruccion es cargada en un registro del CPU para proceder con su ejecucion, y sin importar el paradigma que utilicemos el resultado es siempre el mismo: un archivo ejecutable que contiene codigo maquina.
Sabemos que muchos de los lenguajes mas modernos como Java o C# usan entornos de soporte. Si analizamos este entorno, las instrucciones que se ejecutan deberian tener un formato familiar para el CPU. Esto implica que el orden de las instrucciones ejecutadas por el CPU no se deben mezclar bajo ninguna circunstancia. Veamos el siguiente ejemplo:
int a{4};
std::cout << a << std::endl;
int b{a};
++b;
std::cout << "hola" << std::endl;
b--;
std::cout << (b + 1) << std::endl;
Este codigo mostrara tres valores, primero el de la variable a con el valor iniciado, 4, y luego creamos otra variable y la iniciamos con el valor de la variable anterior. Incrementamos a la nueva variable, mostramos un texto y decrementamos la segunda variable. Y finalmente, procesamos la suma de uno a la segunda variable y la mostramos. Este ejemplo es para mostrar como cada instruccion puede involucrar la lectura de datos desde la memoria, asi como tambien la escritura en la misma. Cuando hablamos de la memoria, mencionamos que posee una jerarquia y esto a su vez nos puede agregar mas dificultad para entender la ejecucion de un programa.
Si examinamos el codigo anterior, la segunda variable asume que el valor de la variable a ya esta cargada de la memoria dentro de un registro de la CPU y la cual usaremos para escribir en la ubicacion de memoria de b. Sobre esto hablamos cuando mencionamos la ubicacion de memoria, y aqui mencionaremos el soporte de concurrencia. Este depende principalmente del modelo del lenguaje, siendo un conjunto de garantias para el acceso simultaneo a la memoria. Si bien, podemos considerar al byte como la unidad direccionable de memoria mas pequeña, la CPU trabaja con WORD y esta la convierte en la unidad mas pequeña que el CPU lee o escribe a la memoria. Analicemos las siguientes dos variables:
char uno;
char dos;
Vamos a suponer que estas dos variables se encuentran en el mismo WORD, esto implica que tanto la lectura como la escritura de cualquiera de ellas involucra la lectura del WORD que contiene a ambas. Este acceso simultaneo a las variables puede derivar en una conducta inesperada. Por esta razon, es que se necesita la garantia del modelo de memoria. El modelo de memoria de C++ nos garantiza que dos threads puedan acceder y actualizar ubicaciones de memoria separadas sin interferirse una a la otra.
Una ubicacion de memoria es un tipo escalar, este es un tipo arimetico, puntero, enumeracion o nullptr_t. La mas larga secuencia de campos de bit adyacentes de longitud no cero se considera una ubicacion de memoria tambien. Esto lleva a pensar que la concurrencia es multithreading, si bien son de naturalezas similares son distintas conceptualmente hablando. Para facilitarnos un poco la cosa, tomemos como ejemplo que estamos viendo la TV y a su vez navegando por internet. Supongamos que estamos viendo nuestro programa favorito, el cual no nos queremos perder, y un amigo nos pide un dato que debemos buscar. No podemos concentrarnos en ambas tareas al mismo tiempo, sino que iremos alternando nuestra atencion a cada tarea por turnos.
Comparado con la concurrencia, estamos haciendo las tareas simultaneamente. Siendo que nuestro cerebro le presta atencion un tiempo al programa de TV, para luego intercambiar al navegador leer un poco al articulo, y luego volver al programa, y asi sucesivamente. Este es un ejemplo simple de tareas corriendo simultaneamente. Como mencionamos anteriormente, las tareas no necesariamente deben correr al mismo tiempo sino que simplemente alternan su atencion. Tomemos como ejemplo, nuestra respiracion. Podemos hacer cualquier tarea mientras respiramos, esto es asi porque lo hacemos en el background. Esto es asi porque nuestro cerebro en ningun momento deja de prestar atencion para respirar, siendo este un claro ejemplo de tareas corriendo en paralelo y la esencia de la concurrencia.
Pero que sucede cuando corremos mas de una aplicacion en la computadora? estan corriendo en paralelo? Tengan por seguro que corren simultaneamente, sin embargo, este va a depender del hardware de la misma. Si bien, hoy las computadoras poseen como minimo dos nucleos, antiguamente, solo poseian uno y como mencionamos la CPU procesa las instrucciones una por una. Pero este inconveniente se soluciono con un tema del cual hablaremos en el proximo post.
En resumen, hoy hemos visto concurrencia, que es, como se compone, para que esta pensado, un par de ejemplos practicos, asi como algunas coincidencias con multithreading. Espero les haya sido de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.


Donation
It’s for maintenance of the site, thanks!
$1.50
