Responder a la pregunta ¿Qué es la programación concurrente? puede llegar hacer algo complejo, principalmente por que hay que tener muy en claro un par de conceptos. Me refieron a: concurrencia, paralelismos y programación estructurada, puntualmente estructura secuencial. Si nunca has escuchado sobre ellos, o, aún te quedan ciertas dudas, no te preocupes, en esta ocasión me gustaría que explicaremos en detalle, y con ejemplos, en qué consiste cada uno de estos conceptos, y de esta forma comprender al cien por ciento que es la programación concurrente, sus ventajas y por su puesto sus desventajas. 🤩🤔
Bien, una vez dicho todo esto ¿Qué les parece si comenzamos? 😎
Programación estructurada
Comencemos con lo sencillo, expliquemos que es la programación estructurada (Un pequeño repaso)
Verás, la programación estructurada no es más que un paradigma de programación. Estoy prácticamente seguro que la gran mayoría de nosotros comenzamos a programar bajo este paradigma, pero ¿De qué se trata? En términos simples la programación estructurada tiene como objetivo mejorar el desarrollo de software recurriendo a únicamente subrutinas y tres estructuras básicas: Secuencial, Selectiva e Iterativa.
En terminos de programación una estructura selectiva hace referencia a condicionales, por ejemplo, un if, else, else if o swith. Para una estructura iterativa estaremos hablando de ciclos, por ejemplo for, foreach, while o do while.
Lo que nos interesa para este post es la estructura secuencial. En programación, una estructura secuencial no es más que la ejecución, en secuencia, de múltiples tareas. Estas tareas serán ejecutadas una tras otra, todas en un orden previamente definido. ☝ Una tarea no podrá ejecutarse antes de otra si en el programa no fue indicado de esa manera.
Veamos un ejemplo.
print('Hola, soy un programa muy simple')
nombre = input('¿Cual es tu nombre? ')
print('Mucho gusto ' + nombre)
print('Adios, fin del programa')
En este ejemplo podemos decir que tenemos cuatro tareas, una por cada línea de código. El orden de ejecución es descendente. El programa no puede imprimir en consola el nombre del usuario sin antes haberlo pedido. De igual forma, el programa no puede despedirse sin antes haber dado la bienvenida. Podemos concluir que este programa es secuencial, ya que cada tarea se ejecuta una tras otra, en un orden.
Concurrencia y Paralelismo
Ahora hablemos de la concurrencia y el paralelismo, conceptos que en muchas ocasiones se confunden y se puede llegar a pensar que se tratan de lo mismo, cuando en realidad no es así, veamos.
La concurrencia es, en esencia, el poder realizar múltiples cosas en el mismo tiempo, pero, no específicamente en paralelo, ¿Qué? 😰
Una de las formas más sencillas de comprender la concurrencia es imaginar a una persona la cual trabaja en múltiples tareas al mismo tiempo, y que rápidamente cambia de una tarea a otra. Por ejemplo, imaginemos a una persona la cual se encuentra programando, realizando cálculos en excel y contestando correos electrónicos, todo esto al mismo tiempo. Dedica un par de segundos a cada tarea, y rápidamente, con un ágil cmd + shift cambia de tarea. 😎
Concluimos que la persona trabaja de forma concurrente. Las tareas que realiza no necesariamente deben seguir un orden, quizás, después de contestar un correo regresa con los cálculos en excel, le dedica un par de segundo, regresa a responder otro correo y finaliza con la codificación del programa, u, otro escenario pudiera ser que después finalizar ciertos cálculos, la persona continua codeando un par de segundos para después responder un par de correos y regresar con los cálculos.
Al contrario que una estructura secuencial, con la concurrencia el orden en que se ejeuctan las tareas importa muy poco. 😉
Bien, veamos un ejemplo en código de concurrencia.
import time
import threading
def codificar():
time.sleep(2)
print(f'Codificando')
def responder_correos():
time.sleep(2)
print(f'Respondiendo correos')
def realizar_calculos():
time.sleep(2)
print(f'Realizar los calculos')
threading.Thread(target=codificar).start()
threading.Thread(target=responder_correos).start()
threading.Thread(target=realizar_calculos).start()
En este caso nuestro programa realiza tres tareas al mismo tiempo. A cada tarea le tomó un máximo de dos segundos ser completada, como se ejecutan de forma concurrente (al mismo tiempo) al programa le toma dos segundo finalizar su ejecución. Por otro lado, si ejecutamos el mismo código, pero ahora de forma secuencial, al programa le tomaría seis segundos finalizar. El tiempo es sin duda considerable.
#secuencial
codificar()
responder_correos()
realizar_calculos()
Bien, ya tenemos claro que es la estructura secuencial y que es la concurrencia, ahora, ¿De qué va el paralelismo?
El paralelismo es el poder de ejecutar dos o más acciones de forma simultánea, en lugar de concurrentemente. Si recordamos, en nuestro ejemplo anterior, el desarrollador realiza tres tareas al mismo tiempo, realizaba calculos programaba y contestaba correos, sin embargo, ninguna de estas tareas se realizaba de forma simultánea.
En nuestro ejemplo, si queremos que las tareas se realicen de forma paralela tendríamos que tener a tres personas trabajando, vaya, una persona por cada tarea, una persona encargada de los cálculos, otra de la codificación y otra respondiendo correos, tres personas trabajando simultáneamente. 😺😺😺
Técnicamente hablando, para llevar a cabo un verdadero paralelismo es necesario que nuestra computadora posea múltiples procesadores, con esto, cada tarea será ejecutada en un procesador diferente, de forma simultánea, de forma paralela.
Para crear paralelismo con Python será necesario utilizar el modulo processing.
import os
import time
import logging
import multiprocessing
logging.basicConfig(level=logging.DEBUG, format='%(message)s')
def new_process(time_to_sleep=0):
logging.info('Comenzamos el proceso hijo!')
time.sleep(time_to_sleep)
logging.info('Terminamos el procesos hijo!')
def main():
process = multiprocessing.Process(target=new_process,
name='proceso-hijo',
args=(1,),
daemon=False)
process.start()
process.join()
logging.info(f'Finalizamos el proceso principal')
if __name__ == '__main__':
main()
La principal diferencia entre concurrencia y paralelismo recae en la forma en que se realizan las tareas. Cuando se ejecutan tareas de forma concurrente a estas se les asigna un x periodo de tiempo antes de cambiar de tarea, será en ese periodo en el cual se inicie, continúe, o se complete la tarea, por otro lado, si ejecutamos tareas en paralelo, las tareas se realizarán de forma simultánea, comenzarán y finalizarán sin interrupciones. 👻
Si deseamos implementar concurrencia en nuestros programas una muy buena idea será utilzar Threads, por otro lado, si deseamos implementar paralelismos optaremos por procesos.
Conclusión
En conclusión y en términos simples, la programación concurrente no es más que la forma en la cual podemos resolver ciertas problemáticas de forma concurrente, es decir, ejecutando múltiples tareas a la misma vez y no de forma secuencial.
En un programa concurrente las tareas puede continuar sin la necesidad que otras comiencen o finalicen.
Si bien es cierto que la programación concurrente acarrea ciertos problemas, principalmente al momento de compartir información entre tareas, también es cierto que si se implementa de forma correcta, podremos, en casos puntuales, mejorar significativamente el performance de nuestras aplicaciones. 😍😎
-
check_circle_outlineMódulo 1 | 32 clases
Introducción
expand_more-
done_all
Clase 1
Introducción
-
done_all
Clase 2
¿Qué es la programación concurrente?
-
done_all
Clase 3
Threads y Procesos
-
done_all
Clase 4
Creación de threads
-
done_all
Clase 5
Múltiples threads
-
done_all
Clase 6
Módulo logging
-
done_all
Clase 7
Thread principal
-
done_all
Clase 8
Dormir a un thread
-
done_all
Clase 9
Callbacks
-
done_all
Clase 10
Programar callbacks
-
done_all
Clase 11
Futuros
-
done_all
Clase 12
Futuros pt2
-
done_all
Clase 13
Método Join
-
done_all
Clase 14
Demonios
-
done_all
Clase 15
Demonios pt2
-
done_all
Clase 16
Módulo threading
-
done_all
Clase 17
Clase Thread
-
done_all
Clase 18
Problema Race condition
-
done_all
Clase 19
Lock
-
done_all
Clase 20
Lock pt2
-
done_all
Clase 21
RLock
-
done_all
Clase 22
Eventos
-
done_all
Clase 23
Eventos pt2
-
done_all
Clase 24
Colas
-
done_all
Clase 25
Problema Productor y consumidor
-
done_all
Clase 26
Pool de threads
-
done_all
Clase 27
Pool bajo contexto
-
done_all
Clase 28
Futuros y pool
-
done_all
Clase 29
Multiples tareas
-
done_all
Clase 30
Tareas completadas
-
done_all
Clase 31
Método map
-
done_all
Clase 32
¿Qué es el GIL en Python?
-
-
check_circle_outlineMódulo 2 | 15 clases
Procesos
expand_more