Tenho tentado entender como os threads funcionam no Python e é difícil encontrar boas informações sobre como eles funcionam. Posso estar perdendo um link ou algo assim, mas parece que a documentação oficial não é muito completa sobre o assunto e não consegui encontrar um bom artigo.
Pelo que eu posso dizer, apenas um thread pode estar em execução por vez, e o thread ativo muda a cada 10 instruções ou mais?
Onde há uma boa explicação ou você pode fornecer uma? Também seria muito bom estar ciente dos problemas comuns que você encontra ao usar threads com Python.
Python é uma linguagem bastante fácil de usar, mas existem ressalvas. A maior coisa que você precisa saber é o Global Interpreter Lock. Isso permite que apenas um thread acesse o intérprete. Isso significa duas coisas: 1) você raramente usa uma instrução de bloqueio em python e 2) se deseja tirar proveito de sistemas com vários processadores, é necessário usar processos separados. EDIT: Também devo salientar que você pode colocar parte do código em C / C ++ se quiser contornar o GIL também.
Portanto, você precisa reconsiderar por que deseja usar threads. Se você deseja paralelizar seu aplicativo para aproveitar as vantagens da arquitetura dual-core, deve considerar dividir seu aplicativo em vários processos.
Se você deseja melhorar a capacidade de resposta, você deve CONSIDERAR o uso de threads. No entanto, existem outras alternativas, nomeadamente microthreading . Existem também algumas estruturas que você deve examinar:
fonte
Abaixo está um exemplo básico de threading. Ele irá gerar 20 threads; cada thread produzirá seu número de thread. Execute-o e observe a ordem de impressão.
import threading class Foo (threading.Thread): def __init__(self,x): self.__x = x threading.Thread.__init__(self) def run (self): print str(self.__x) for x in xrange(20): Foo(x).start()
Como você indicou, os threads do Python são implementados por meio de divisão de tempo. É assim que eles obtêm o efeito "paralelo".
Em meu exemplo, minha classe Foo estende thread, então implemento o
run
método, que é para onde vai o código que você gostaria de executar em um thread. Para iniciar o thread, você chamastart()
no objeto thread, que invocará automaticamente orun
método ...Claro, isso é apenas o básico. Eventualmente, você desejará aprender sobre semáforos, mutexes e bloqueios para sincronização de threads e passagem de mensagens.
fonte
Use threads em python se os trabalhadores individuais estiverem fazendo operações vinculadas a E / S. Se você está tentando escalar em vários núcleos em uma máquina, encontre uma boa estrutura IPC para python ou escolha uma linguagem diferente.
fonte
Nota: sempre que menciono,
thread
quero dizer especificamente threads em python até que seja explicitamente declarado.Threads funcionam de maneira um pouco diferente em python se você estiver vindo de
C/C++
segundo plano. No python, apenas um thread pode estar em execução em um determinado momento. Isso significa que os threads no python não podem realmente aproveitar o poder de vários núcleos de processamento, pois, por design, não é possível que os threads sejam executados paralelamente em vários núcleos.Como o gerenciamento de memória em python não é seguro para threads, cada thread requer um acesso exclusivo às estruturas de dados no interpretador python. Esse acesso exclusivo é adquirido por um mecanismo chamado (bloqueio global do interpretador) .
GIL
Why does python use GIL?
A fim de evitar que vários threads acessem o estado do interpretador simultaneamente e corrompam o estado do interpretador.
A ideia é sempre que um thread está sendo executado (mesmo que seja o thread principal) , um GIL é adquirido e após algum intervalo de tempo predefinido o GIL é liberado pelo thread atual e readquirido por algum outro thread (se houver).
Why not simply remove GIL?
Não que seja impossível remover o GIL, é apenas que, no processo de fazer isso, acabamos colocando vários bloqueios dentro do interpretador para serializar o acesso, o que torna até mesmo um único aplicativo encadeado menos performante.
portanto, o custo de remoção do GIL é compensado pela redução do desempenho de um aplicativo thread único, o que nunca é desejado.
So when does thread switching occurs in python?
A troca de thread ocorre quando GIL é lançado. Então, quando GIL é lançado? Existem dois cenários a serem considerados.
Se um Thread estiver fazendo operações vinculadas à CPU (processamento de imagem Ex).
Em versões mais antigas do python, a troca de thread costumava ocorrer após um não fixo de instruções do python. Por padrão, era definido como
100
. Descobriu-se que não é uma política muito boa decidir quando a troca deve ocorrer, pois o tempo gasto na execução de uma única instrução pode ir de milissegundo até mesmo um segundo. Portanto, liberar GIL após todas as100
instruções, independentemente do tempo que levem para executar, é uma política ruim.Em novas versões, em vez de usar a contagem de instruções como uma métrica para alternar o thread, um intervalo de tempo configurável é usado. O intervalo de troca padrão é de 5 milissegundos. Você pode obter o intervalo de troca atual usando
sys.getswitchinterval()
. Isso pode ser alterado usandosys.setswitchinterval()
Se um thread está fazendo algumas operações vinculadas de IO (acesso ao sistema de arquivos Ex ou
IO de rede)
GIL é liberado sempre que o thread está aguardando a conclusão de alguma operação de E / S.
Which thread to switch to next?
O intérprete não tem seu próprio agendador. Qual thread é agendado no final do intervalo é uma decisão do sistema operacional. .
fonte
Uma solução fácil para o GIL é o módulo de multiprocessamento . Ele pode ser usado como um substituto para o módulo de threading, mas usa vários processos de intérprete em vez de threads. Por causa disso, há um pouco mais de sobrecarga do que simples threading para coisas simples, mas oferece a vantagem de paralelização real, se necessário. Ele também pode ser facilmente dimensionado para várias máquinas físicas.
Se você precisa de paralelização em grande escala, então eu procuraria mais, mas se você quiser apenas dimensionar para todos os núcleos de um computador ou alguns diferentes sem todo o trabalho que seria necessário para implementar uma estrutura mais abrangente, então isso é para você .
fonte
Tente lembrar que o GIL está configurado para pesquisar de vez em quando para mostrar a aparência de várias tarefas. Essa configuração pode ser ajustada, mas ofereço a sugestão de que deve haver algum trabalho que os encadeamentos estejam realizando ou muitas mudanças de contexto causarão problemas.
Eu iria mais longe a ponto de sugerir vários pais em processadores e tentar manter trabalhos semelhantes no (s) mesmo (s) núcleo (s).
fonte