Preciso de uma fila na qual vários threads possam colocar coisas e vários threads possam ler.
O Python possui pelo menos duas classes de fila, Queue.Queue e collections.deque, sendo que o primeiro parece usar o último internamente. Ambos afirmam ser seguros para threads na documentação.
No entanto, os documentos da fila também declaram:
collections.deque é uma implementação alternativa de filas ilimitadas com operações atômicas append () e popleft () rápidas que não requerem bloqueio.
Acho que não entendo bem: isso significa que o deque não é totalmente seguro para threads, afinal?
Se for, posso não entender completamente a diferença entre as duas classes. Percebo que a fila adiciona funcionalidade de bloqueio. Por outro lado, perde alguns recursos de deque, como suporte para o operador.
Acessar o objeto deque interno diretamente, é
x em Queue (). deque
discussão segura?
Além disso, por que o Queue emprega um mutex para suas operações quando o deque já é seguro para threads?
fonte
RuntimeError: deque mutated during iteration
é o que você poderia estar recebendo está usando uma compartilhadadeque
entre vários fio e nenhum bloqueio ...deque
tempo, mesmo iterando no mesmo thread. O único motivo pelo qual você não pode obter esse erroQueue
é queQueue
ele não suporta iteração.Respostas:
Queue.Queue
ecollections.deque
servir a propósitos diferentes. O Queue.Queue destina-se a permitir que diferentes threads se comuniquem usando mensagens / dados em fila, ao passo quecollections.deque
é simplesmente uma estrutura de dados. É por isso queQueue.Queue
tem métodos comoput_nowait()
,get_nowait()
ejoin()
, enquantocollections.deque
não.Queue.Queue
não se destina a ser usado como uma coleção, e é por isso que não possui os gostos doin
operador.Tudo se resume a isso: se você tem vários threads e deseja que eles possam se comunicar sem a necessidade de bloqueios, está procurando
Queue.Queue
; se você quiser apenas uma fila ou uma fila dupla como uma estrutura de dados, usecollections.deque
.Finalmente, acessar e manipular o deque interno de a
Queue.Queue
está brincando com fogo - você realmente não quer fazer isso.fonte
Queue.Queue
, ele usadeque
sob o capô.collections.deque
é uma coleção, enquantoQueue.Queue
é um mecanismo de comunicação. A sobrecargaQueue.Queue
é torná-lo seguro. Usardeque
para se comunicar entre threads só leva a corridas dolorosas. Sempredeque
que for seguro, é um feliz acidente de como o intérprete é implementado, e não algo em que se possa confiar. É por isso queQueue.Queue
existe em primeiro lugar.deque is threadsafe by accident due to the existence of GIL
; é verdade quedeque
depende do GIL para garantir a segurança do thread - mas não éby accident
. A documentação oficial do python afirma claramente quedeque
pop*
/append*
métodos são seguros para threads. Portanto, qualquer implementação python válida deve fornecer a mesma garantia (as implementações sem GIL terão que descobrir como fazer isso sem o GIL). Você pode confiar nessas garantias.deque
para a comunicação. Se você envolverpop
umtry/except
, você terminará com um loop ocupado consumindo uma quantidade enorme de CPU apenas aguardando novos dados. Isso parece uma abordagem terrivelmente ineficiente em comparação com as chamadas de bloqueio oferecidas porQueue
, que garantem que o thread que está aguardando os dados entre em suspensão e não perca tempo com a CPU.Queue.Queue
isso, porque está escrito usandocollections.deque
: hg.python.org/cpython/file/2.7/Lib/Queue.py - ele usa variáveis de condição para permitirdeque
que os envoltórios sejam acessados com eficiência sobre os limites da rosca com segurança e eficiência. A explicação de como você usaria umdeque
para comunicação está ali na fonte.Se tudo que você procura é uma maneira segura de encadear a transferência de objetos entre encadeamentos , ambos funcionam (ambos para FIFO e LIFO). Para FIFO:
Queue.put()
eQueue.get()
são seguros para threadsdeque.append()
edeque.popleft()
são seguros para threadsNota:
deque
podem não ser seguras para threads, não tenho certeza.deque
não bloqueiapop()
ou,popleft()
portanto, você não pode basear seu fluxo de encadeamento no bloqueio até que um novo item chegue.No entanto, parece que o deque tem uma vantagem de eficiência significativa . Aqui estão alguns resultados de benchmark em segundos usando o CPython 2.7.3 para inserir e remover 100k itens
Aqui está o código de referência:
fonte
deque
podem não ser seguras para threads". De onde você tira isso?Para obter informações, há um ticket Python referenciado para deque thread-safety ( https://bugs.python.org/issue15329 ). Título "esclarecer quais métodos de deque são seguros para threads"
Bottom line here: https://bugs.python.org/issue15329#msg199368
De qualquer forma, se você não tem 100% de certeza e prefere confiabilidade ao desempenho, basta colocar um Lock;)
fonte
Todos os métodos de elemento único ativados
deque
são atômicos e seguros para threads. Todos os outros métodos também são seguros para threads. Coisas comolen(dq)
,dq[4]
produzem valores corretos momentâneos. Mas pense, por exemplodq.extend(mylist)
: você não tem garantia de que todos os elementosmylist
são arquivados em uma linha quando outros threads também acrescentam elementos do mesmo lado - mas isso geralmente não é um requisito na comunicação entre threads e para a tarefa questionada.Portanto, a
deque
é ~ 20x mais rápido do queQueue
(que usa umdeque
sob o capô) e, a menos que você não precise da API de sincronização "confortável" (bloqueio / tempo limite), amaxsize
obediência estrita ou a "Substituir esses métodos (_put, _get, .. ) para implementar o comportamento de subclasse de outras organizações de filas " , ou quando você cuida dessas coisas sozinho, um negócio simplesdeque
é bom e eficiente para a comunicação entre threads de alta velocidade.De fato, o uso intenso de um método extra mutex e extra,
._get()
etc.,Queue.py
é devido a restrições de compatibilidade com versões anteriores, excesso de design passado e falta de cuidados em fornecer uma solução eficiente para esse importante problema de gargalo de velocidade na comunicação entre threads. Uma lista foi usada em versões mais antigas do Python - mas mesmo list.append () /. Pop (0) era & é atômica e segura para threads ...fonte
Adicionando
notify_all()
a cadadeque
append
epopleft
resulta em muito piores resultados paradeque
que a melhoria 20x alcançado por padrãodeque
de comportamento :@ Jonathan modifica um pouco seu código e eu recebo o benchmark usando o cPython 3.6.2 e adiciono a condição no loop deque para simular o comportamento da fila.
E parece que o desempenho limitado por esta função
condition.notify_all()
fonte
deque
é seguro para threads. "operações que não exigem bloqueio" significa que você não precisa fazer o bloqueio sozinho,deque
ele cuida disso.Examinando a
Queue
fonte, o deque interno é chamadoself.queue
e usa um mutex para acessadores e mutações, portanto, nãoQueue().queue
é seguro para uso em threads.Se você estiver procurando por um operador "in", uma fila ou fila provavelmente não é a estrutura de dados mais apropriada para o seu problema.
fonte
(parece que não tenho reputação para comentar ...) Você precisa ter cuidado com quais métodos do deque você usa em diferentes threads.
deque.get () parece ser seguro para threads, mas descobri que fazer
pode falhar se outro segmento estiver adicionando itens ao mesmo tempo. Eu recebi um RuntimeException que reclamou "deque foi alterado durante a iteração".
Verifique collectionsmodule.c para ver quais operações são afetadas por este
fonte
>>> di = {1:None} >>> for x in di: del di[x]
while
loop.