Documentação do Python 3.7
Gostaria também de destacar a seguinte citação da documentação do Pythonthreading
:
Detalhes da implementação do CPython: No CPython, devido ao Bloqueio Global de Intérpretes, apenas um encadeamento pode executar o código Python de uma só vez (mesmo que certas bibliotecas orientadas ao desempenho possam superar essa limitação). Se você deseja que seu aplicativo faça melhor uso dos recursos computacionais de máquinas com vários núcleos, é recomendável usar multiprocessing
ou concurrent.futures.ProcessPoolExecutor
. No entanto, o encadeamento ainda é um modelo apropriado se você deseja executar várias tarefas associadas a E / S simultaneamente.
Isso vincula à entradaglobal interpreter lock
do Glossário, na qual explica que o GIL implica que o paralelismo encadeado em Python não é adequado para tarefas vinculadas à CPU :
O mecanismo usado pelo interpretador CPython para garantir que apenas um encadeamento execute o bytecode do Python por vez. Isso simplifica a implementação do CPython, tornando o modelo de objeto (incluindo tipos internos críticos, como dict) implicitamente seguro contra acesso simultâneo. O bloqueio de todo o intérprete facilita que o intérprete seja multiencadeado, às custas de grande parte do paralelismo proporcionado pelas máquinas com vários processadores.
No entanto, alguns módulos de extensão, padrão ou de terceiros, são projetados para liberar o GIL ao executar tarefas intensivas em computação, como compactação ou hash. Além disso, o GIL é sempre liberado ao fazer E / S.
Os esforços anteriores para criar um intérprete de "encadeamento livre" (que bloqueia os dados compartilhados com uma granularidade muito mais fina) não foram bem-sucedidos porque o desempenho sofreu no caso comum de processador único. Acredita-se que superar esse problema de desempenho tornaria a implementação muito mais complicada e, portanto, mais cara de manter.
Esta citação também implica que os dictos e, portanto, a atribuição de variáveis também são seguros para threads como um detalhe de implementação do CPython:
A seguir, os documentos do multiprocessing
pacote explicam como ele supera o GIL ao gerar processo, enquanto expõe uma interface semelhante à de threading
:
multiprocessamento é um pacote que suporta processos de geração usando uma API semelhante ao módulo de segmentação. O pacote de multiprocessamento oferece simultaneidade local e remota, efetivamente ultrapassando o bloqueio global de intérpretes usando subprocessos em vez de threads. Devido a isso, o módulo de multiprocessamento permite que o programador aproveite totalmente vários processadores em uma determinada máquina. Ele roda em Unix e Windows.
E os documentos paraconcurrent.futures.ProcessPoolExecutor
explicar que ele usa multiprocessing
como back-end:
A classe ProcessPoolExecutor é uma subclasse Executor que usa um conjunto de processos para executar chamadas de forma assíncrona. ProcessPoolExecutor usa o módulo de multiprocessamento, o que lhe permite desviar o bloqueio global de intérpretes, mas também significa que apenas objetos selecionáveis podem ser executados e retornados.
que deve ser contrastado com a outra classe base ThreadPoolExecutor
que usa threads em vez de processos
ThreadPoolExecutor é uma subclasse de Executor que usa um pool de threads para executar chamadas de forma assíncrona.
da qual concluímos que ThreadPoolExecutor
é adequado apenas para tarefas vinculadas de E / S, enquanto ProcessPoolExecutor
também pode lidar com tarefas vinculadas à CPU.
A pergunta a seguir pergunta por que o GIL existe em primeiro lugar: Por que o bloqueio global de intérpretes?
Experiências de processo versus encadeamento
No Multiprocessing vs Threading Python , fiz uma análise experimental do processo vs threads no Python.
Visualização rápida dos resultados: