Qual é exatamente a função do Global Interpreter Lock do Python? As outras linguagens compiladas para bytecode empregam um mecanismo semelhante?
Qual é exatamente a função do Global Interpreter Lock do Python? As outras linguagens compiladas para bytecode empregam um mecanismo semelhante?
Respostas:
Em geral, para qualquer problema de segurança de thread, você precisará proteger suas estruturas de dados internas com bloqueios. Isso pode ser feito com vários níveis de granularidade.
Você pode usar um bloqueio de granulação fina, onde cada estrutura separada tem seu próprio bloqueio.
Você pode usar o bloqueio de granulação grossa, onde um bloqueio protege tudo (a abordagem GIL).
Existem vários prós e contras de cada método. O bloqueio refinado permite maior paralelismo - duas threads podem ser executadas em paralelo quando não compartilham nenhum recurso. No entanto, há uma sobrecarga administrativa muito maior. Para cada linha de código, você pode precisar adquirir e liberar vários bloqueios.
A abordagem de granulação grossa é o oposto. Dois encadeamentos não podem ser executados ao mesmo tempo, mas um encadeamento individual será executado mais rápido porque não está realizando muitos registros. Em última análise, tudo se resume a uma troca entre velocidade de thread único e paralelismo.
Houve algumas tentativas de remover o GIL no python, mas a sobrecarga extra para máquinas de thread único geralmente era muito grande. Alguns casos podem realmente ser mais lentos, mesmo em máquinas com vários processadores, devido à contenção de bloqueio.
As outras linguagens compiladas para bytecode empregam um mecanismo semelhante?
Isso varia e provavelmente não deve ser considerado uma propriedade de linguagem tanto quanto uma propriedade de implementação. Por exemplo, existem implementações Python, como Jython e IronPython, que usam a abordagem de threading de sua VM subjacente, em vez de uma abordagem GIL. Além disso, a próxima versão de Ruby parece estar se movendo em direção à introdução de um GIL.
O seguinte é do Manual de referência oficial da API Python / C :
O interpretador Python não é totalmente thread-safe. Para oferecer suporte a programas Python multi-threaded, há um bloqueio global que deve ser mantido pelo thread atual antes que ele possa acessar objetos Python com segurança. Sem o bloqueio, mesmo as operações mais simples podem causar problemas em um programa multi-threaded: por exemplo, quando duas threads incrementam simultaneamente a contagem de referência do mesmo objeto, a contagem de referência pode acabar sendo incrementada apenas uma vez em vez de duas vezes.
Portanto, existe a regra de que apenas o thread que adquiriu o bloqueio do interpretador global pode operar em objetos Python ou chamar funções de API Python / C. Para oferecer suporte a programas Python multi-threaded, o interpretador regularmente libera e readquire o bloqueio - por padrão, a cada 100 instruções de bytecode (isso pode ser alterado com sys.setcheckinterval ()). O bloqueio também é liberado e readquirido em torno de operações de E / S potencialmente bloqueadoras, como ler ou gravar um arquivo, para que outras threads possam ser executadas enquanto a thread que solicita a E / S está esperando a conclusão da operação de E / S.
Acho que resume o problema muito bem.
O bloqueio de interpretador global é um grande bloqueio do tipo mutex que protege os contadores de referência de serem bloqueados. Se você está escrevendo código Python puro, tudo isso acontece nos bastidores, mas se você embutir Python em C, então você pode ter que pegar / liberar explicitamente o bloqueio.
Este mecanismo não está relacionado ao Python sendo compilado para bytecode. Não é necessário para Java. Na verdade, ele nem mesmo é necessário para Jython (python compilado para jvm).
veja também esta questão
Python, como perl 5, não foi projetado desde o início para ser thread-safe. Threads foram enxertados após o fato, portanto, o bloqueio do intérprete global é usado para manter a exclusão mútua para onde apenas um thread está executando o código em um determinado momento nas entranhas do interpretador.
Threads individuais do Python são multitarefas cooperativamente pelo próprio interpretador, alternando o bloqueio de vez em quando.
É necessário travar você mesmo quando estiver conversando com o Python a partir de C, quando outras threads do Python estiverem ativas para 'aceitar' este protocolo e certificar-se de que nada de inseguro aconteça nas suas costas.
Outros sistemas que possuem uma herança de segmento único que posteriormente evoluiu para sistemas de segmento múltiplo geralmente possuem algum mecanismo desse tipo. Por exemplo, o kernel do Linux tem o "Big Kernel Lock" de seus primeiros dias de SMP. Gradualmente, ao longo do tempo, conforme o desempenho de multithreading se torna um problema, há uma tendência de tentar quebrar esses tipos de bloqueios em pedaços menores ou substituí-los por algoritmos sem bloqueio e estruturas de dados sempre que possível para maximizar o rendimento.
reiserfs
- a única razão real que eu sei sobre isso).
Com relação à sua segunda pergunta, nem todas as linguagens de script usam isso, mas isso apenas as torna menos poderosas. Por exemplo, os tópicos em Ruby são verdes e não nativos.
No Python, os threads são nativos e o GIL apenas impede que eles sejam executados em núcleos diferentes.
Em Perl, os threads são ainda piores. Eles apenas copiam o interpretador inteiro e estão longe de ser tão utilizáveis quanto no Python.
Talvez este artigo da BDFL ajude.