Bienvenidos sean a este post, hoy hablaremos sobre un tema bastante complejo.
Cuando se habla de ejecucion concurrente se toma como una desviacion del paradigma habitual de ejecucion de un solo subproceso, para resumirlo es muy similar a lo visto en otros lenguajes conocidos como multithreading o multiproceso pero aca es mucho mas complejo y amplio pero no es la idea terminar siendo un experto en este tema sino simplemente tener un conocimiento sobre este tema, hablemos un poco sobre la diferencia entre concurrencia y paralelismo.
Un error muy comun es ser consideradas como la misma cosa pero hay una diferencia entre ambas:
- Concurrencia, es la habilidad para correr multiples cosas al mismo tiempo pero no necesariamente en paralelo
- Paralelismo, es la habilidad para hacer multiples cosas al mismo tiempo
Un ejemplo practico es imaginar que esto es como un teatro donde tendremos dos tipos de entradas, una vip y otra normal, y solo tenemos una persona que verifica el tema de los tickets, por lo tanto para evitar que se bloqueen las filas primero chequea una de la fila vip y luego otra de la normal, con el tiempo ambas filas son procesadas y a este proceso se lo denomina concurrencia, en cambio supongamos que tenemos otra persona o empleado que se acerca a ayudar al primer empleado por lo que ahora tenemos una persona para cada fila, de esta forma las filas seran procesadas por separado, esto es denominado paralelismo.
En los ordenadores actuales es muy usual que posean mas de un nucleo, como minino dos y como maximo siete (por lo menos hasta el momento de crear este post), pero que es un nucleo? Un nucleo es una unidad de procesamiento independiente que pertenece a un procesador, teniendo mas de uno significa que la CPU tiene la capacidad fisica para actualmente ejecutar tareas en paralelo, dentro de cada nucleo hay una alternancia constancia de flujos de trabajo, lo cual es una ejecucion concurrente.
En los posts anteriores comentamos que es un thread y un proceso, las formas que tienen de trabajarlos, por lo tanto a partir de este post nos centraremos en como manejarlos, veamos el primer tema.
Iniciando un thread
Para esto crearemos un ejemplo, para nuestro primer paso importaremos el modulo para manejar los threads:
>>> import threading
Nuestro siguiente paso sera crear una funcion:
>>> def suma_y_producto(a, b):
... s, p = a + b, a * b
... print(f'{a} + {b} = {s}, {a} * {b} = {p}')
...
>>>
Esta es una funcion para hacer la suma y producto de dos valores informados, creamos dos variables (s para la suma y p para el producto), luego haremos las operaciones comentadas, por ultimo mostraremos los valores informados y los realizados, nuestro siguiente paso sera crear un nuevo objeto:
>>> t = threading.Thread(target=suma_y_producto,
... name='SumaProd', args=(3,7))
>>>
En este objeto usaremos Thread de threading para crear a nuestro thread y en este pasaremos tres atributos:
- target sera para la funcion que pasaremos al thread
- name sera un nombre de identificacion para el thread
- args son los valores que pasaremos como argumento a la funcion
Con nuestro thread creado pasemos a iniciarlo:
>>> t.start()
3 + 7 = 10, 3 * 7 = 21
>>>
Lo hacemos mediante start y como pueden ver nos devolvio la salida de la funcion, este es un ejemplo bastante simple como para entender el concepto pero vamos a crear otro ejemplo para ver un par de detalles mas, para este caso crearemos un archivo llamado start_con_info.py y agregaremos el siguiente codigo:
start_con_info.py
import threading
from time import sleep
def suma_y_producto(a, b):
sleep(.2)
mostrar_actual()
s, p = a + b, a * b
print(f'{a} + {b} = {s}, {a} * {b} = {p}')
def estado(t):
if t.is_alive():
print(f'Thread {t.name} esta activo.')
else:
print(f'Thread {t.name} ha terminado.')
def mostrar_actual():
print('El thread actual es {}.'.format(
threading.current_thread()
))
print('Threads: {}'.format(
list(threading.enumerate())))
mostrar_actual()
t = threading.Thread(target=suma_y_producto,
name='SumPro', args=(3, 7))
t.start()
estado(t)
t.join()
estado(t)
Este es muy similar al anterior pero con algunas nuevas funciones, importaremos a threading pero tambien importaremos a sleep del modulo time, tenemos la funcion suma_y_producto que es igual al ejemplo anterior pero le agregamos un sleep para una pequeña demora y luego llamamos a la funcion mostrar_actual que definiremos en un minuto, el resto es igual a lo visto anteriormente, despues tenemos la nueva funcion llamado estado, la cual recibe el thread generado, en este bloque tenemos un condicional donde verifica por medio del condicional si t esta activo (is_alive) y en caso de ser verdadero mostramos un mensaje indicando el nombre del thread y que esta activo, de lo contrario nos indicara el nombre y que ha terminado, la siguiente funcion es mostrar_actual y dentro de esta mostremos dos datos, el primero sera cual es thread actual por medio de current_thread y el segundo nos enumera los threads por medio de enumerate, con esto completamos la segunda funcion.
Solo nos resta un par de instrucciones mas, lo primero que haremos es ejecutar mostrar_actual para ver cual es el thread actual, luego volvemos a crear un objeto de tipo Thread igual que en el caso anterior, para despues iniciarlo, ver su estado, luego usamos el join para terminarlo y por ultimo volvemos a mostrar el estado, con esto tenemos nuestro codigo explicado, probemos a ver como es su salida:
tinchicus@dbn001vrt:~/lenguajes/python$ python3 start_con_info.py
El thread actual es <_MainThread(MainThread, started 140535773738816)>.
Threads: [<_MainThread(MainThread, started 140535773738816)>]
Thread SumPro esta activo.
El thread actual es <Thread(SumPro, started 140535768188672)>.
Threads: [<_MainThread(MainThread, started 140535773738816)>, <Thread(SumPro, started 140535768188672)>]
3 + 7 = 10, 3 * 7 = 21
Thread SumPro ha terminado.
tinchicus@dbn001vrt:~/lenguajes/python$
Veamos la salida, primero nos devuelve solamente el thread principal, y nos enumera la unica que existe, recuerden que lo primero que hicimos es llamar a mostrar_actual, luego cuando iniciamos el objeto t nos informa que el nuevo thread esta activo, nos muestra que es el actual y luego nos muestra los dos existentes, y muestra el resultado de la funcion, por ultimo al ejecutar el join y eliminarlo, nos avisa que se ha terminado, con esto hemos visto como iniciar un thread y como ver algunas estadisticas, pasemos al siguiente tema.
Iniciando un proceso
Iniciar un proceso no difiere mucho de un thread, para ello tomaremos el primer ejemplo y lo modificaremos para que en lugar de usar un thread sea un proceso, para ello desde nuestro interprete debemos importar lo siguiente:
>>> import multiprocessing
Este es el modulo que nos permite trabajar con los procesos, luego volveremos a definir la funcion suma_y_producto:
>>> def suma_y_producto(a, b):
... s, p = a + b, a * b
... print(f'{a} + {b} = {s}, {a} * {b} = {p}')
...
>>>
Esta es la misma funcion que vimos en el primer ejemplo, vamos a crear un nuevo objeto:
>>> p = multiprocessing.Process(target=suma_y_producto,
... name='SumProdProc', args=(7,9))
>>>
Es muy similar a cuando creamos un trhead pero esta vez usamos a Process y le pasamos exactamente los mismos argumentos, donde target es para la funcion, name para identificar al proceso y args son los argumentos para la funcion, por ultimo lo ejecutaremos con esta funcion:
>>> p.start()
>>> 7 + 9 = 16, 7 * 9 = 63
Por medio de start iniciamos el proceso y nos devolvera la salida de la funcion, como pueden ver son muy parecidos pero la diferencia estara en como manejara la funcion ya que uno es un thread y el otro un proceso, con las diferencias que hemos mencionado en este post pero que comparten los mismos parametros para crearlos y para iniciarlos.
En resumen, hoy hemos visto una introduccion a ejecucion concurrente, hemos hablado como trabaja tanto con los procesos como con los threads, despues hemos visto como por medio de esta metodologia de trabajo nos permite iniciar un thread, y un ejemplo mas complejo para ver mas datos del mismo, luego vimos como iniciar un proceso para observar que son muy similares a la hora de trabajarlos pero con sus respectivas diferencias una vez procesados, espero les haya sido util 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.


Donación
Es para mantenimento del sitio, gracias!
$1.50
