Como os threads funcionam em Python e quais são as armadilhas específicas comuns dos threads do Python?

StackOverflow https://stackoverflow.com/questions/31340

  •  09-06-2019
  •  | 
  •  

Pergunta

Tenho tentado entender como os threads funcionam em Python e é difícil encontrar boas informações sobre como eles funcionam.Talvez esteja faltando 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 sei, 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.

Foi útil?

Solução

Sim, por causa do Global Interpreter Lock (GIL), só pode ser executado um thread por vez.Aqui estão alguns links com alguns insights sobre isso:

No último link, uma citação interessante:

Deixe-me explicar o que tudo isso significa.Os threads são executados dentro da mesma máquina virtual e, portanto, são executados na mesma máquina física.Os processos podem ser executados na mesma máquina física ou em outra máquina física.Se você arquitetar seu aplicativo em torno de threads, não fez nada para acessar várias máquinas.Portanto, você pode escalar para tantos núcleos na máquina única (que será um pouco com o tempo), mas, para realmente atingir as escalas da Web, você precisará resolver o problema de múltiplas máquinas de qualquer maneira.

Se você quiser usar vários núcleos, processamento de py define uma API baseada em processo para fazer paralelização real.O PEP também inclui alguns benchmarks interessantes.

Outras dicas

Python é uma linguagem bastante fácil de usar, mas há ressalvas.A maior coisa que você precisa saber é o Global Interpreter Lock.Isso permite que apenas um thread acesse o interpretador.Isso significa duas coisas:1) você raramente usa uma instrução lock em python e 2) se quiser aproveitar as vantagens dos sistemas multiprocessadores, precisará usar processos separados.EDITAR:Devo também 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 quiser paralelizar seu aplicativo para aproveitar as vantagens da arquitetura dual-core, você precisa considerar dividir seu aplicativo em vários processos.

Se você quiser melhorar a capacidade de resposta, CONSIDERAR o uso de threads.Existem outras alternativas, nomeadamente microthreading.Existem também algumas estruturas que você deve examinar:

Abaixo está um exemplo básico de threading.Irá gerar 20 threads;cada thread produzirá seu número de thread.Execute-o e observe a ordem em que são impressos.

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ê sugeriu, os threads do Python são implementados por meio de divisão de tempo.É assim que eles obtêm o efeito “paralelo”.

No meu exemplo, minha classe Foo estende o 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 tópico você chama start() no objeto thread, que invocará automaticamente o run 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.

Use threads em python se os trabalhadores individuais estiverem realizando operações vinculadas a E/S.Se você estiver tentando escalar vários núcleos em uma máquina, encontre um bom CIP framework para python ou escolha uma linguagem diferente.

Observação: onde quer que eu mencione thread quero dizer especificamente tópicos em python até que seja explicitamente declarado.

Threads funcionam de maneira um pouco diferente em python se você estiver vindo de C/C++ fundo.Em python, apenas um thread pode estar em estado de execução em um determinado momento. Isso significa que os threads em 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 é thread-safe, cada thread requer um acesso exclusivo às estruturas de dados no interpretador python. Esse acesso exclusivo é adquirido por um mecanismo chamado GIL (bloqueio global do interpretador).

Why does python use GIL?

Para evitar que vários threads acessem o estado do intérprete simultaneamente e corrompam o estado do intérprete.

A ideia é sempre que um thread está sendo executado (mesmo que seja o tópico principal), um GIL é adquirido e, após algum intervalo predefinido de tempo, o GIL é liberado pelo encadeamento atual e reagendido por algum outro thread (se houver).

Why not simply remove GIL?

Não é que seja impossível remover o GIL, é apenas que, ao fazê-lo, acabamos colocando vários bloqueios dentro do interpretador para serializar o acesso, o que torna até mesmo um único aplicativo encadeado com menos desempenho.

portanto, o custo da remoção do GIL é compensado pela redução do desempenho de um único aplicativo encadeado, o que nunca é desejado.

So when does thread switching occurs in python?

A troca de thread ocorre quando o GIL é lançado. Então, quando o GIL é lançado?Existem dois cenários a serem considerados.

Se um Thread estiver realizando operações vinculadas à CPU (processamento de imagem Ex).

Nas versões mais antigas do python, a troca de thread costumava ocorrer após um número fixo de instruções do python. 100. Acabou que não é uma política muito boa decidir quando a mudança deve ocorrer, pois o tempo gasto executando uma única instrução pode muito descontroladamente de milissegundos até um segundo. 100 instruções, independentemente do tempo que levam para serem executadas, é uma política ruim.

Nas novas versões, em vez de usar a contagem de instruções como métrica para alternar thread, é usado um intervalo de tempo configurável.O intervalo de troca padrão é de 5 milissegundos. Você pode obter o intervalo de troca atual usando sys.getswitchinterval().Isto pode ser alterado usando sys.setswitchinterval()

Se um Thread estiver realizando algumas operações vinculadas a IO (Ex acesso ao sistema de arquivos ou
E/S de rede)

GIL é liberado sempre que o thread está aguardando a conclusão de alguma operação de IO.

Which thread to switch to next?

O interpretador não possui seu próprio escalonador. Qual thread será agendado no final do intervalo é uma decisão do sistema operacional..

Uma solução fácil para o GIL é a multiprocessamento módulo.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 o threading simples para coisas simples, mas oferece a vantagem da paralelização real, se necessário.Ele também pode ser facilmente dimensionado para várias máquinas físicas.

Se você precisa de uma paralelização realmente em grande escala, eu procuraria mais, mas se você deseja apenas escalar para todos os núcleos de um computador ou alguns diferentes, sem todo o trabalho necessário para implementar uma estrutura mais abrangente, então isso é para você .

Tente lembrar que o GIL está configurado para fazer pesquisas de vez em quando para mostrar a aparência de múltiplas tarefas.Essa configuração pode ser ajustada, mas sugiro que os threads estejam trabalhando ou que muitas mudanças de contexto causarão problemas.

Eu chegaria ao ponto de sugerir vários pais em processadores e tentar manter trabalhos semelhantes no(s) mesmo(s) núcleo(s).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top