Logo de Código Facilito
  • Inicio
  • Replays
  • Iniciar sesión
  • Crear cuenta
  • Explorar cursos
  • Bootcamps
  • Precios
  • Blog

¡Califica el Curso de programación concurrente en Python!

Selecciona la calificación de 1 a 5 estrellas

Reporta un error

Curso Curso de programación concurrente en Python

Video ¿Qué es el GIL en Python?

Tipo de error

Algo salió mal al cargar el vídeo

El vídeo no pudo cargarse, hemos enviado un reporte al equipo de desarrollo, para poder solucionarlo a la brevedad.

Mientras solucionamos el problema, intenta lo siguiente para solucionar el error:

  • Recarga la página
  • Intenta reiniciar tu navegador y luego vuelve a reproducir el vídeo
  • Vacía el caché de tu navegador
  • Intenta reproducir con las extensiones del navegador deshabilitadas
  • Intenta con un navegador distinto
  • Si el problema persiste contáctanos en Discord
home Ir al inicio report_problem Reportar falla star Valorar curso

En la actualidad la mayoría de los lenguajes de programación soportan la programación concurrente y la programación en paralelo, pudiendo así ejecutar diferentes tareas en diferentes procesadores, sin embargo, con Python esto no es así. 😰 Sí, lo sé, cuesta creer, pero es la verdad. Con Python al utilizar Threads nunca seremos capaces de lograr un verdadero paralelismo, ya que el lenguaje está diseñado para que un thread y solo un thread pueda ejecutarse a la vez.

No me crees 😓, veamos un ejemplo. En este caso he definido una función que lo unico que hace es decrecer su parametro(number) hasta llegar a 0.

import time

def countdown(number):
    while number > 0:
        number -=1

if __name__ == '__main__':
    start = time.time()

    count = 100000000
    countdown(count)

    print(f'Tiempo transcurrido {time.time() - start }')

Si ejecuto mi script, tal y como se encuentra ahora, con un solo thread y de forma secuencial, le tomará un apróximado de 6 segundos finalizar. Todo bien, nada nuevo.

Ahora, hagamos los mismo pero con dos threads. En teoria, al los threads ejecutarse de forma concurrente al script le debería tomar poco más de 6 segundos finalizar, quizás, unos milisegundos más, en teoria. 😳

import time
import threading

def countdown(number):
    while number > 0:
        number -=1

if __name__ == '__main__':
    start = time.time()

    count = 100000000

    t1 = threading.Thread(target=countdown, args=(count,))
    t2 = threading.Thread(target=countdown, args=(count,))

    t1.start()
    t2.start()

    t1.join()
    t2.join()

    print(f'Tiempo transcurrido {time.time() - start }')

A mi escript, de forma concurrente le toma 12 segundos finalizar, sí, el doble de tiempo apesar de utilizar threads. 😲 Esto gracias al GIL.

Seguro no te lo esperabas, de hecho yo tampoco, siendo Python uno de los lenguajes más populares y utilizados en todos el mundo como es posible que el mismo lenguaje limite su potencial, pues bien esto se debe a gracias a GIL Global interepter Lock, el villano en todo esto, o quizás no, veamos, por que es tan importante GIL en Python.

GIL

Verás, el GIL ,Global interepter Lock de Python, permite que sólo un thread tome el control del intérprete, es decir, que solo un thread puede estar en ejecución a la vez. Esto rara vez tiene repercusiones para quienes desarrollamos programas utilizando solo el thread principal, es más, es probable que nunca nos hayamos dado cuenta, pero, para aquellas personas que desarrollan de forma concurrente este si puede llegar a ser un dolor de cabeza.

Pero, ¿Si GIL genera cuellos de botella, por qué se introdujo en primera instancia y por qué simplemente no se quita? Muy buenas preguntas, y para responderlas es necesario comprender como funciona Python internamente.

En términos simples Python posee un concepto llamado conteo de referencias, el cual le permite conocer al intérprete cuando un objeto está siendo utilizando y cuando no. Es algo bastante simple.

Por ejemplo, si mi variable A, posee referencias, por lo menos una o más, entonces, se concluye que la variable está siendo utilizada en alguna parte del programa. Por otro lado, si la variable no posee referencia, se concluye que no se está utilizando, y es allí donde entra el recolector de basura y libera la memoria.

Veamos un ejemplo.

import sys

A = 'Hola, soy una referencia'

sys.getrefcount(A)
>>> 2

En este casa obtenemos como resultado dos, ya que la variable A cuenta con dos referencias, la asignación y en el llamado a la función.

Bastante sencillo ¿no?

Pues bien, el trabajo de GIL es impedir que múltiples threads decrementen la referencia de algún objeto mientras otros están haciendo uso de ella. Como la naturaleza de un thread es trabajar de forma concurrente, en teoría, es posible que un thread le indique al intérprete que una variable se ha dejado de utilizar, cuando realmente otro thread aun sigue trabajando con ella.

Para evitar este problema se implementó GIL, permitiendo que sólo un thread tome el control del intérprete.

GIL no solo se resolvio el problema del conteo de referencias, también hizo que la implementación de Python sea mucho más sencilla, a la vez que incrementa la velocidad de ejecución cuando trabajamos con un único thread.

En palabras de Larry Hastings: la decisión del diseño de GIL es una de las cosas que hizo que Python fuera tan popular como lo es hoy.

Ahora, ya sabemos que GIL previene que espacios en memoria sean liberados cuando aún están siendo utilizados, ok, pero, ¿no hay forma en crear algún otro mecanismo en el recolector de basura para evitar el problema? pues, dejame decirte que Guido van Rossum ,creador de Python dice que GIL esta aquí para quedarse, esto lo explica muy bien en un post al cual te invito le heches un ojo.

Pero en esencia, eliminar GIL traería muchos más problemas que ventajas. Programas que ya se encuentran en producción podrían dejar de comportarse como lo hacen ahora para simplemente fallar. Así que, mejor no. (Yo puedo vivir con GIL)

Multiprocesamientos

Pero no te preocupes, no todo está perdido. Si realmente queremos que las tareas se ejecuten de forma paralela debemos optar por el multiprocesamiento sobre el multithreading; de esta forma cada proceso tendrá su propio intérprete y podrá ejecutarse de manera independiente, logrando así evitar el cuello de botella de GIL y aprovechando todo el potencial de nuestros equipos.

La mejor forma de trabajar procesos en Python es sin duda con la librería multiprocessing.

Aquí un pequeño ejemplo de como crear nuestros propios Procesos. Haz la prueba por ti mismo y verás el resultado.

import time
import threading
import multiprocessing

def countdown(number):
    while number > 0:
        number -=1

if __name__ == '__main__':
    start = time.time()

    count = 100000000

    t1 = multiprocessing.Process(target=countdown, args=(count,))
    t2 = multiprocessing.Process(target=countdown, args=(count,))

    t1.start()
    t2.start()

    t1.join()
    t2.join()

    print(f'Tiempo transcurrido {time.time() - start }')

Si quieres conocer más acerca de GIL, tambíen conocido por la comunidad como: an an “infamous” feature of Python, te comparto una conferencia de Larry Hastings.

  • check_circle_outline
    Mó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_outline
    Módulo 2 | 15 clases

    Procesos

    expand_more

¿Qué es el GIL en Python?

arrow_back Siguiente arrow_forward
Curso de programación concurrente en Python