Estou tentando usar multiprocessing
a Pool.map()
função de dividir o trabalho simultaneamente. Quando uso o seguinte código, ele funciona bem:
import multiprocessing
def f(x):
return x*x
def go():
pool = multiprocessing.Pool(processes=4)
print pool.map(f, range(10))
if __name__== '__main__' :
go()
No entanto, quando eu o uso em uma abordagem mais orientada a objetos, ele não funciona. A mensagem de erro exibida é:
PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup
__builtin__.instancemethod failed
Isso ocorre quando o seguinte é o meu programa principal:
import someClass
if __name__== '__main__' :
sc = someClass.someClass()
sc.go()
e a seguinte é a minha someClass
turma:
import multiprocessing
class someClass(object):
def __init__(self):
pass
def f(self, x):
return x*x
def go(self):
pool = multiprocessing.Pool(processes=4)
print pool.map(self.f, range(10))
Alguém sabe qual poderia ser o problema ou uma maneira fácil de contornar isso?
python
multithreading
multiprocessing
pickle
pool
ventolin
fonte
fonte
PicklingError: Can't pickle <class 'function'>: attribute lookup builtins.function failed
Respostas:
O problema é que o multiprocessamento deve separar as coisas para separá-las entre os processos, e os métodos vinculados não são selecionáveis. A solução alternativa (se você considera "fácil" ou não ;-) é adicionar a infraestrutura ao seu programa para permitir a seleção de tais métodos, registrando-a no método de biblioteca padrão copy_reg .
Por exemplo, a contribuição de Steven Bethard para esse thread (no final do thread) mostra uma abordagem perfeitamente viável para permitir a seleção / remoção de pickp do método
copy_reg
.fonte
_pickle_method
retornosself._unpickle_method
, um método vinculado; então é claro que pickle agora tenta escolher ISSO - e faz o que você pediu: ligando_pickle_method
recursivamente. Ou seja, aoOO
inserir o código dessa maneira, você inevitavelmente introduziu uma recursão infinita. Sugiro voltar ao código de Steven (e não adorar no altar do OO quando não for apropriado: muitas coisas em Python são melhor executadas de maneira mais funcional, e essa é uma).Todas essas soluções são feias porque o multiprocessamento e a decapagem são interrompidos e limitados, a menos que você pule para fora da biblioteca padrão.
Se você usar uma bifurcação de
multiprocessing
chamadapathos.multiprocesssing
, poderá usar diretamente classes e métodos de classe nasmap
funções do multiprocessamento . Isso ocorre porquedill
é usado em vez depickle
oucPickle
edill
pode serializar quase tudo em python.pathos.multiprocessing
também fornece uma função de mapa assíncrona ... e podemap
funcionar com vários argumentos (por exemplomap(math.pow, [1,2,3], [4,5,6])
)Veja: O que o multiprocessamento e o endro podem fazer juntos?
e: http://matthewrocklin.com/blog/work/2013/12/05/Parallelism-and-Serialization/
E apenas para ser explícito, você pode fazer exatamente o que deseja fazer em primeiro lugar, e pode fazê-lo a partir do intérprete, se quiser.
Obtenha o código aqui: https://github.com/uqfoundation/pathos
fonte
pathos
autor. A versão a que você está se referindo tem vários anos. Experimente a versão no github, Você pode usarpathos.pp
ou github.com/uqfoundation/ppft .pip install setuptools
entãopip install git+https://github.com/uqfoundation/pathos.git@master
. Isso obterá as dependências apropriadas. Uma nova versão está quase pronta ... agora quase tudopathos
também roda no Windows e é3.x
compatível.Você também pode definir um
__call__()
método dentro do seusomeClass()
, que chamasomeClass.go()
e depois passa uma instânciasomeClass()
para o pool. Este objeto é selecionável e funciona bem (para mim) ...fonte
__call__()
? Acho que a sua resposta pode ser a mais limpa - estou lutando para entender esse erro e, pela primeira vez, venho atender. A propósito, também esta resposta ajuda a esclarecer o que o multiprocessamento faz: [ stackoverflow.com/a/20789937/305883] #Algumas limitações à solução de Steven Bethard:
Quando você registra seu método de classe como uma função, surpreendentemente o destruidor de sua classe é chamado toda vez que o processamento do método é concluído. Portanto, se você tiver uma instância da sua classe que chama n vezes seu método, os membros podem desaparecer entre duas execuções e você pode receber uma mensagem
malloc: *** error for object 0x...: pointer being freed was not allocated
(por exemplo, abrir arquivo de membro) oupure virtual method called, terminate called without an active exception
(o que significa que o tempo de vida de um objeto de membro que usei foi menor que o que eu pensei). Eu consegui isso ao lidar com n maior que o tamanho da piscina. Aqui está um pequeno exemplo:Resultado:
O
__call__
método não é tão equivalente, porque [Nenhum, ...] é lido a partir dos resultados:Portanto, nenhum dos dois métodos é satisfatório ...
fonte
None
volta porque sua definição de__call__
está faltando oreturn
: deveria serreturn self.process_obj(i)
.Há outro atalho que você pode usar, embora possa ser ineficiente, dependendo do que está em suas instâncias de classe.
Como todos disseram, o problema é que o
multiprocessing
código precisa selecionar as coisas que envia para os subprocessos que foram iniciados, e o selecionador não usa métodos de instância.No entanto, em vez de enviar o método da instância, você pode enviar a instância da classe real, mais o nome da função a ser chamada, para uma função comum que então usa
getattr
para chamar o método da instância, criando o método vinculado noPool
subprocesso. Isso é semelhante à definição de um__call__
método, exceto que você pode chamar mais de uma função de membro.Roubar o código do @ EricH. Da sua resposta e anotá-lo um pouco (redigitei-o, portanto, todo o nome muda e tal, por algum motivo isso parecia mais fácil do que copiar e colar :-)) para ilustrar toda a mágica:
A saída mostra que, de fato, o construtor é chamado uma vez (no pid original) e o destruidor é chamado 9 vezes (uma vez para cada cópia feita = 2 ou 3 vezes por processo de trabalho conjunto, conforme necessário, mais uma vez no original processo). Isso geralmente é bom, como neste caso, já que o seletor padrão faz uma cópia de toda a instância e (semi-) a preenche secretamente - nesse caso, fazendo:
- é por isso que, embora o destruidor seja chamado oito vezes nos três processos de trabalho, ele é reduzido de 1 a 0 a cada vez - mas é claro que você ainda pode ter problemas dessa maneira. Se necessário, você pode fornecer o seu próprio
__setstate__
:neste caso, por exemplo.
fonte
Você também pode definir um
__call__()
método dentro do seusomeClass()
, que chamasomeClass.go()
e depois passa uma instânciasomeClass()
para o pool. Este objeto é selecionável e funciona bem (para mim) ...fonte
A solução da parisjohn acima funciona bem comigo. Além disso, o código parece limpo e fácil de entender. No meu caso, existem algumas funções para chamar usando Pool, então modifiquei o código de parisjohn um pouco abaixo. Eu fiz call para poder chamar várias funções, e os nomes das funções são passados no argumento dict de
go()
:fonte
Uma solução potencialmente trivial para isso é mudar para o uso
multiprocessing.dummy
. Esta é uma implementação baseada em thread da interface de multiprocessamento que parece não ter esse problema no Python 2.7. Não tenho muita experiência aqui, mas essa mudança rápida de importação me permitiu chamar apply_async em um método de classe.Alguns bons recursos sobre
multiprocessing.dummy
:https://docs.python.org/2/library/multiprocessing.html#module-multiprocessing.dummy
http://chriskiehl.com/article/parallelism-in-one-line/
fonte
Nesse caso simples, onde
someClass.f
não está herdando nenhum dado da classe e não anexando nada à classe, uma solução possível seria separá-f
la para que ela possa ser decapada:fonte
Por que não usar func separado?
fonte
Corri para o mesmo problema, mas descobri que existe um codificador JSON que pode ser usado para mover esses objetos entre processos.
Use isto para criar sua lista:
Em seguida, na função mapeada, use isso para recuperar o objeto:
fonte
Atualização: no dia em que este artigo foi escrito, os nomeadosTuples são selecionáveis (começando com python 2.7)
O problema aqui é que os processos filho não conseguem importar a classe do objeto - nesse caso, a classe P -, no caso de um projeto de modelo múltiplo, a classe P deve ser importável em qualquer lugar em que o processo filho seja usado
uma solução rápida é torná-lo importável, afetando-o para globais ()
fonte