logo

O que é GIL em Python? Bloqueio global de intérprete

Este tutorial será focado em um dos tópicos importantes do Python, GIL. Também abordaremos como o GIL impacta o desempenho dos programas Python com a implementação do código. Antes de mergulhar neste tópico, vamos ter uma ideia básica do GIL.

GIL ou bloqueio global de intérprete

Python Global Interpreter Lock ou GIL é uma parte importante da programação multithreading. É um tipo de bloqueio de processo usado ao trabalhar com vários processos. Dá o controle a apenas um thread. Geralmente, Python usa um único thread para executar um único processo. Obtemos o mesmo resultado de desempenho dos processos single-threaded e multi-threaded usando o GIL. Ele restringe a obtenção de multithreading em Python porque evita os threads e funciona como um único thread.

Nota - Python não suporta multithreading porque os pacotes de threading não nos permitem usar vários núcleos de CPU.

Por que os desenvolvedores Python usam GIL?

Python fornece o recurso exclusivo de contador de referência, que é usado para gerenciamento de memória. O contador de referências conta o número total de referências feitas internamente em Python para atribuir um valor a um objeto de dados. Quando as contagens de referência chegam a zero, a memória atribuída ao objeto é liberada. Vejamos o exemplo abaixo.

Exemplo -

boa sorte
 import sys a = [] b = a sys.getrefcount(a) 

A principal preocupação com a variável de contagem de referência é que ela pode ser afetada quando dois ou três threads tentam aumentar ou diminuir seu valor simultaneamente. É conhecido como condição de corrida. Se essa condição ocorrer, poderá ocorrer vazamento de memória que nunca será liberada. Pode travar ou apresentar bugs no programa Python.

GIL nos ajuda a remover tal situação usando bloqueios para todas as estruturas de dados compartilhadas entre threads, para que não sejam alteradas de forma inconsistente. Python fornece uma maneira fácil de implementar o GIL, pois lida com gerenciamento de memória thread-safe. GIL exige a oferta de um único bloqueio para um thread para processamento em Python. Ele aumenta o desempenho de um programa de thread único, pois apenas um bloqueio precisa ser manipulado. Também ajuda a criar qualquer programa vinculado à CPU e evita a condição de deadlocks.

int para string java

O impacto em programas Python multithread

Há uma diferença entre os limites da CPU em seu desempenho e os limites de E/S para um programa Python típico ou qualquer programa de computador. Os programas vinculados à CPU geralmente levam a CPU ao seu limite. Esses programas são geralmente usados ​​para cálculos matemáticos, como multiplicações de matrizes, queima, processamento de imagens, etc.

Programas vinculados a E/S são aqueles programas que gastam tempo para obter entradas/saídas que podem ser geradas pelo usuário, arquivo, banco de dados, rede, etc. Esses programas precisam esperar um tempo significativo até que a fonte forneça a entrada. Por outro lado, a fonte também possui seu próprio tempo de processamento. Por exemplo - um usuário está pensando no que inserir como entrada.

Vamos entender o exemplo a seguir.

Exemplo -

 import time from threading import Thread COUNT = 100000000 def countdown(num): while num>0: num -= 1 start_time = time.time() countdown(COUNT) end_time = time.time() print('Time taken in seconds -', end_time - start_time) 

Saída:

 Time taken in seconds - 7.422671556472778 

Agora modificamos o código acima executando os dois threads.

Exemplo - 2:

 import time from threading import Thread COUNT = 100000000 def countdown(num): while num>0: num -= 1 thread1 = Thread(target=countdown, args=(COUNT//2,)) thread2 = Thread(target=countdown, args=(COUNT//2,)) start_time = time.time() thread1.start() thread2.start() thread1.join() thread2.join() end_time = time.time() print('Time taken in seconds -', end_time - start_time) 

Saída:

 Time taken in seconds - 6.90830135345459 

Como podemos ver que ambos os códigos demoraram o mesmo tempo para serem concluídos. GIL impediu que os threads vinculados à CPU fossem executados em paralelo no segundo código.

inkscape x gimp

Por que o GIL ainda não foi removido?

Muitos programadores reclamam disso, mas o Python não consegue trazer mudanças tão significativas quanto a remoção do GIL. Outra razão é que o GIL não foi melhorado até agora. Se mudar no Python 3, criará alguns problemas sérios. Em vez de remover o GIL, o conceito do GIL pode melhorar. De acordo com Guido van Rossom -

'Eu aceitaria um conjunto de patches no Py3k apenas se o desempenho de um programa de thread único (e de um programa multithread, mas vinculado a E/S) não diminuir'.

Existem também muitos métodos disponíveis que resolvem o mesmo problema resolvido pelo GIL, mas são difíceis de implementar.

Como lidar com o GIL do Python

Usar multiprocessamento é a maneira mais adequada de impedir o programa de GIL. Python oferece vários intérpretes para cada processo a ser executado, portanto, nesse cenário, o thread único é fornecido para cada processo em multiprocessamento. Vamos entender o exemplo a seguir.

Exemplo -

comprimento da string bash
 from multiprocessing import Pool import time COUNT = 50000000 def countdown(num): while num>0: num -= 1 if __name__ == '__main__': pool = Pool(processes=2) start_time = time.time() r1 = pool.apply_async(countdown, [COUNT//2]) r2 = pool.apply_async(countdown, [COUNT//2]) pool.close() pool.join() end_time = time.time() print('Time taken in seconds -', end_time - start_time) 

Saída:

 Time taken in seconds - 3.3707828521728516 

Pode parecer que um desempenho decente aumentou, mas o gerenciamento de processos tem suas próprias despesas gerais e vários processos são mais pesados ​​que vários threads.

Conclusão

Neste tutorial, discutimos o GIL e como podemos usá-lo. Ele dá o controle para um único thread ser executado de cada vez. Este tutorial também abordou por que o GIL é importante para programadores Python.