Percebo que geralmente é sugerido o uso de filas com vários threads, em vez de listas e .pop()
. Isso ocorre porque as listas não são seguras para threads, ou por algum outro motivo?
155
Percebo que geralmente é sugerido o uso de filas com vários threads, em vez de listas e .pop()
. Isso ocorre porque as listas não são seguras para threads, ou por algum outro motivo?
Respostas:
As próprias listas são seguras para threads. No CPython, o GIL protege contra acessos simultâneos a eles, e outras implementações tomam cuidado para usar um bloqueio refinado ou um tipo de dados sincronizado para suas implementações de lista. No entanto, enquanto as próprias listas não podem ficar corrompidas por tentativas de acesso simultâneo, os dados da lista não são protegidos. Por exemplo:
não é garantido que realmente aumente L [0] em um se outro thread fizer a mesma coisa, porque
+=
não é uma operação atômica. (Muito, muito poucas operações no Python são realmente atômicas, porque a maioria delas pode fazer com que o código arbitrário do Python seja chamado.) Você deve usar as Filas porque, se usar apenas uma lista desprotegida, poderá obter ou excluir o item errado devido à raça condições.fonte
Para esclarecer um ponto da excelente resposta de Thomas, deve-se mencionar que
append()
é seguro para threads.Isso ocorre porque não há preocupação de que os dados que estão sendo lidos estejam no mesmo local quando formos gravá -los. A
append()
operação não lê dados, apenas grava dados na lista.fonte
PyList_Append
é feita em um bloqueio GIL. É dada uma referência a um objeto para acrescentar. O conteúdo desse objeto pode ser alterado depois de avaliado e antes de a chamadaPyList_Append
. Mas ainda será o mesmo objeto e anexado com segurança (se você o fizerlst.append(x); ok = lst[-1] is x
,ok
pode ser falso, é claro). O código que você referencia não lê do objeto anexado, exceto para INCREF. Ele lê e pode realocar a lista anexada.L[0] += x
ele executará um__getitem__
onL
e depois__setitem__
onL
- se oL
suporte__iadd__
fizer as coisas de maneira um pouco diferente na interface do objeto, mas ainda existem duas operações separadas noL
nível do interpretador python (você as verá no bytecode compilado). Issoappend
é feito em uma chamada de método único no bytecode.remove
?Aqui está uma lista abrangente e não exaustiva de exemplos de
list
operações e se são ou não seguros para threads. Esperando obter uma resposta sobre aobj in a_list
construção da linguagem aqui .fonte
Recentemente, tive este caso em que precisava anexar a uma lista continuamente em um thread, percorrer os itens e verificar se o item estava pronto; era um AsyncResult no meu caso e removê-lo da lista apenas se estivesse pronto. Não foi possível encontrar nenhum exemplo que demonstrasse meu problema claramente. Aqui está um exemplo demonstrando a adição à lista em um thread continuamente e a remoção da mesma lista em outro thread continuamente. A versão defeituosa roda facilmente em números menores, mas mantém os números grandes o suficiente e executa uma algumas vezes e você verá o erro
A versão FLAWED
Saída quando ERRO
Versão que usa bloqueios
Resultado
Conclusão
Conforme mencionado nas respostas anteriores, enquanto o ato de acrescentar ou estourar elementos da lista em si é seguro para threads, o que não é seguro para threads é quando você acrescenta um thread e aparece em outro
fonte
with r:
) em vez de chamar explicitamenter.acquire()
er.release()