notifications Notificaciones

Marcar todas como leídas

Ver más

Obtén acceso ilimitado a éste y todos nuestros cursos con tu suscripción Premium

¿Qué es la programación concurrente?

Abrir temario playlist_play

Lo lees en 4 Min.

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. 😍😎

Acerca del curso