Celery: As tarefas não podem iniciar subprocessos

Criado em 29 nov. 2013  ·  68Comentários  ·  Fonte: celery/celery

A partir do Celery 3.1.0, o pool de processos ( celery.concurrency.prefork , antigo celery.concurrency.processes ) usa processos daemon para realizar tarefas.

Os processos daemon não têm permissão para criar processos filhos e, como resultado, as tarefas que usam o pacote multiprocessing não estão funcionando:

[2013-11-29 14:27:48,297: ERROR/MainProcess] Task app.add[e5d184c0-471f-4fc4-804c-f760178d4847] raised exception: AssertionError('daemonic processes are not allowed to have children',)
Traceback (most recent call last):
  File "/Users/aromanovich/Envs/celery3.1/lib/python2.7/site-packages/celery/app/trace.py", line 218, in trace_task
    R = retval = fun(*args, **kwargs)
  File "/Users/aromanovich/Envs/celery3.1/lib/python2.7/site-packages/celery/app/trace.py", line 398, in __protected_call__
    return self.run(*args, **kwargs)
  File "/Users/aromanovich/Projects/celery/app.py", line 10, in add
    manager = multiprocessing.Manager()
  File "/usr/local/Cellar/python/2.7.6/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/__init__.py", line 99, in Manager
    m.start()
  File "/usr/local/Cellar/python/2.7.6/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/managers.py", line 524, in start
    self._process.start()
  File "/usr/local/Cellar/python/2.7.6/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/process.py", line 124, in start
    'daemonic processes are not allowed to have children'
Not a Bug

Comentários muito úteis

@thedrow
Você entendeu mal. Em dobro.
Nossa preocupação não é que você não tenha o recurso (isso é perfeitamente compreensível e, infelizmente, um caso muito comum em software livre). Nossa preocupação é que o ticket esteja fechado por causa disso, não é assim que funciona o ticket.
Não somos 'infelizes', estamos chocados.

Todos 68 comentários

Isso não mudou entre 3.0 e 3.1, então não sei por que você receberia esse erro agora e não antes.

É assim que esse erro pode ser reproduzido.

app.py:

import multiprocessing
from celery import Celery

app = Celery(__name__, broker='amqp://192.168.33.40')
@app.task
def f():
    manager = multiprocessing.Manager()

sendtask.py:

import app

app.f.delay()

Eu executo o trabalhador usando o seguinte comando: celery worker -A app.app -l debug .

Com o Celery 3.0.24, a tarefa é bem-sucedida:

[2013-12-02 20:43:56,454: INFO/MainProcess] Task app.f[bcaab028-dbec-43a8-9259-ff7c35ff13d0] 
succeeded in 0.0169339179993s: None

Com o aipo 3.1.5 não:

[2013-12-02 20:48:38,946: ERROR/MainProcess] Task app.f[c9f1cdd3-ae38-493e-b7c7-b9636ed473d0] 
raised exception: AssertionError('daemonic processes are not allowed to have children',)

Meu entendimento do problema é o seguinte: celery.concurrency.prefork.TaskPool uses celery.concurrency.asynpool.AsynPool ; AsynPool herda de billiard.pool.Pool que gera processos de trabalho daemon e AsynPool não sobrescreve este comportamento. Mas você está certo, este esquema não parece ter mudado entre 3.0 e 3.1, então estou confuso também :)

E parece que não estou sozinho com esse problema: http://stackoverflow.com/questions/20149421/threads-in-celery-3-1-5

Uma diferença é que o processo de trabalho agora é uma subclasse de 'Processo', onde antes usava o argumento de função: Process(target=) , talvez haja uma diferença nos valores padrão para essas abordagens.

multiprocessamento e versões antigas de jogos de bilhar daemon=True :
https://github.com/celery/billiard/blob/2.7/billiard/pool.py#L904

E é o mesmo na versão mais recente:
https://github.com/celery/billiard/blob/3.3/billiard/pool.py#L1039

Eu acho que o processo de tarefa sendo um daemon apresenta uma limitação séria para a implementação de tarefas.
Escrevi uma tarefa que usa multiprocessamento para acelerar as operações vinculadas à CPU. Tudo funciona bem quando eu inicio um trabalhador em um terminal da seguinte maneira:

trabalhador de aipo --app = tarefas -Q wb -l info --concurrency = 1

Mas quando uso o script celeryd para iniciar um trabalhador, recebo esta exceção:
AssertionError: processos daemônicos não podem ter filhos

Eu descobri o que causou a mudança no comportamento.
As tarefas são executadas usando processos daemon em 3.0 e 3.1, mas até celery / billiard @ 4c32d2e e https://github.com/celery/billiard/commit/c676b94aa4144349b11ab31c82296a5d804909c9 multiprocessing módulo não estava ciente disso e, portanto, estava permitindo a criação de subprocessos.

No meu entendimento, havia um bug anterior à versão 3.1 (tarefas eram permitidas para criar subprocessos, o que poderia resultar em estado órfão) e agora esse bug foi corrigido.

A decisão de não permitir a bifurcação dos processos daemon do python parece bastante arbitrária para mim. Embora reconheça a boa fé disso, sinto que devo ter controle total sobre esse comportamento, se quiser.

Estar vinculado a um processo por tarefa parece ser uma limitação séria para mim. Pensamentos?

Eu me pergunto por que essa limitação está lá em primeiro lugar, um aviso que posso entender, mas desautorizá-lo completamente parece bobagem quando você é perfeitamente capaz de bifurcar processos usando outros meios.

@ask , seria possível inicializar o processo de trabalho do aipo com a bandeira daemon sendo False? Ou tornar isso configurável?

@ilyastam parece que estávamos comentando ao mesmo tempo

Concordo que parece uma limitação arbitrária, mas gostaria de saber a razão por trás de adicioná-la em primeiro lugar.

Esta é uma armadilha bem conhecida em sistemas posix, mas ainda é permitida. Você pode limpar os processos filhos em um manipulador de sinal, embora isso não o proteja contra o SIGKILL.

Acho que devemos remover a limitação do bilhar, embora isso divergisse do comportamento de multiprocessamento. Você ainda pode criar processos filho usando o módulo subpocess ou usando a chamada de baixo nível fork , então usuários avançados devem ser capazes de criar instâncias billiard.Process filho.

@ilyastam Deve ser capaz de remover a instrução de aumento, não precisa tornar os processos "não-daemon"

Ou seja, os processos daemon terão permissão para criar processos filhos, mesmo que não seja capaz de coletá-los,
que é como posix funciona de qualquer maneira.

A propósito, note que isso não é raise , é uma instrução assert, que será removida se o python for iniciado com o PYTHONOPTIMIZE envvar ou o argumento -O .

Bilhar 3.3.0.11 está no PyPI incluindo esta mudança

@ask, obrigado. Alguma ideia de qual versão de aipo terá essa melhora?

multiprocessing afirma explicitamente que o processo daemon não tem permissão para criar subprocessos e explica por quê. Quanto a mim, esta instrução assert parece mais ter sido colocada aqui como um atalho para raise (as pessoas costumam fazer isso).

Essa limitação está documentada e não acho que seja uma boa ideia o Celery fazer um monkey patch silencioso em multiprocessing e removê-lo. Isso pode levar a consequências realmente inesperadas e prejudiciais.

Posso pensar no seguinte exemplo (pode parecer um pouco artificial, no entanto):

@app.task
def f():
    p = multiprocessing.Pool(3)
    p.map_async(time.sleep, [1000, 1000, 1000])

Sendo executado como uma função Python simples, este código funciona corretamente. Mas, sendo executado como uma tarefa do Celery (usando o Celery versão 3.0. *), Ele deixa três subprocessos que ficarão suspensos para sempre; quando o trabalhador do aipo sair, esses subprocessos se tornarão órfãos.

Não explica o porquê, apenas afirma o comportamento unix que você esperaria ao iniciar um processo filho-filho. Mesmo que seja uma limitação infame no Unix, não impede as pessoas de fazê-lo. Isso não é diferente de
iniciar um subprocess.Popen processo, ou mesmo chamar fork() para iniciar um novo processo. Então, por que deveria ser ilegal?

A maneira de fazer seu exemplo:

from billiard import Pool
from multiprocessing.util import Finalize

_finalizers = []

@app.task
def f():
    p = billiard.Pool(3)
    _finalizers.append(Finalize(p, p.terminate))
   try:
       p.map_async(time.sleep, [1000, 1000, 1000])
       p.close()
       p.join()
   finally:
       p.terminate()

Para matar (-9) isso você teria que também matar -9 os processos filhos, mas isso é algo que você terá
a considerar para todos os processos unix.

Não que eu defenda a criação de um Pool para cada tarefa, mas não vejo por que os usuários, que sabem o que são
fazendo, não deve ser permitido iniciar processos a partir de uma tarefa.

Além disso, não fazemos monkey patch em nada, isso é uma mudança apenas no bilhar.

Além disso, não fazemos monkey patch em nada, isso é uma mudança apenas no bilhar.

Por "monkey patching" quero dizer esta atribuição, que substitui multiprocessing._current_process por uma instância de billiard.process.Process : https://github.com/celery/billiard/blob/master/billiard/process.py # L53.

Concordo que não há nada de errado em iniciar processos filho-filho se eles forem tratados da maneira certa (como no seu exemplo). Meu ponto é que multiprocessing não foi escrito dessa forma e não devemos ignorar suas limitações de _implementação_.

@aromanovich Não pode ser escrito de outra forma, não é uma limitação do multiprocessamento é uma limitação do unix.

Ele define _current_process para que a variável de formato dos módulos de registro processName funcione, e o objeto do processo de bilhar tenha a mesma API que o objeto do processo de multiprocessamento, portanto, é seguro definir o processo atual.

E aliás, você teria que usar bilhar para que a limitação fosse removida, usar multiprocessamento ainda levantará a exceção.

Também pode corrigir esse problema usando esta abordagem:
http://stackoverflow.com/questions/6974695/python-process-pool-non-daemonic
O que permitiria aos usuários continuar usando o módulo de multiprocessamento, evitando este problema:
https://github.com/celery/billiard/issues/99

Recebo este erro ao chamar uma tarefa de tecido @parallel de dentro de uma tarefa de aipo.

@celery.task
def dostuff():
   execute(fabfile.push_settings, sid=site['sid'])

<strong i="7">@parallel</strong>
@roles(environment)
def push_settings(sid):
  #do stuff

@frodopwns use ENV
exportar PYTHONOPTIMIZE = 1
para remover esta declaração. você precisa lidar com todas as coisas.

@xiaods Acho que resolvi esse problema com algo assim:

@worker_process_init.connect
def configure_workers(sender=None, conf=None, **kwargs):
    Crypto.Random.atfork()

Problema

Tenho uma tarefa que calcula alguns dados e carrega um classificador scikit-learn para fazer previsões com base nesses dados. Quando executo a tarefa sozinha, está tudo bem, mas quando a executo usando o Celery, recebo um erro quando a tarefa tenta carregar o classificador em conserva:

[2015-07-17 21:23:51,299: ERROR/MainProcess] Task app.f[329d0da4-2e0e-4e1f-8148-d64f47750b1f] raised unexpected: AttributeError("'Worker' object has no attribute '_config'",)
Traceback (most recent call last):
  File "/home/username/anaconda3/lib/python3.4/site-packages/celery/app/trace.py", line 240, in trace_task
    R = retval = fun(*args, **kwargs)
  File "/home/username/anaconda3/lib/python3.4/site-packages/celery/app/trace.py", line 438, in __protected_call__
    return self.run(*args, **kwargs)
  File "/home/username/working/playground/celery/app.py", line 11, in f
    clf = pickle.load(open('clf.pickle', 'rb'))
  File "/home/username/anaconda3/lib/python3.4/site-packages/sklearn/ensemble/__init__.py", line 6, in <module>
    from .base import BaseEnsemble
  File "/home/username/anaconda3/lib/python3.4/site-packages/sklearn/ensemble/base.py", line 13, in <module>
    from ..externals.joblib import cpu_count
  File "/home/username/anaconda3/lib/python3.4/site-packages/sklearn/externals/joblib/__init__.py", line 112, in <module>
    from .parallel import Parallel
  File "/home/username/anaconda3/lib/python3.4/site-packages/sklearn/externals/joblib/parallel.py", line 23, in <module>
    from ._multiprocessing_helpers import mp
  File "/home/username/anaconda3/lib/python3.4/site-packages/sklearn/externals/joblib/_multiprocessing_helpers.py", line 25, in <module>
    _sem = mp.Semaphore()
  File "/home/username/anaconda3/lib/python3.4/multiprocessing/context.py", line 81, in Semaphore
    return Semaphore(value, ctx=self.get_context())
  File "/home/username/anaconda3/lib/python3.4/multiprocessing/synchronize.py", line 127, in __init__
    SemLock.__init__(self, SEMAPHORE, value, SEM_VALUE_MAX, ctx=ctx)
  File "/home/username/anaconda3/lib/python3.4/multiprocessing/synchronize.py", line 59, in __init__
    kind, value, maxvalue, self._make_name(),
  File "/home/username/anaconda3/lib/python3.4/multiprocessing/synchronize.py", line 117, in _make_name
    return '%s-%s' % (process.current_process()._config['semprefix'],
AttributeError: 'Worker' object has no attribute '_config'

Reproduzir

Crie um classificador vazio e salve-o como pickle:

import pickle
from sklearn.ensemble import GradientBoostingClassifier
clf = GradientBoostingClassifier()
pickle.dump(clf, open('clf.pickle', 'wb'))

Crie um aplicativo simples ( app.py ):

import pickle
import sklearn
from celery import Celery

app = Celery(__name__, broker='amqp://localhost//')

@app.task
def f():
    print('hello')
    clf = pickle.load(open('clf.pickle', 'rb'))
    print(clf)

Comece o trabalhador de aipo:

celery -A app worker --loglevel=debug

Execute o aplicativo:

python -c "from app import f; f.delay()"

Mensagem de erro:

...
AttributeError: 'Worker' object has no attribute '_config'

Solução

Acho que deveria haver uma opção para "monkeypatch" aipo para permitir que tarefas iniciem subprocessos, especialmente se tal "recurso" existisse no passado. No momento, as pessoas estão simplesmente mudando para outras estruturas quando encontram este problema: http://stackoverflow.com/questions/27904162/using-multiprocessing-pool-from-celery-task-raises-exception. Aqui está outro exemplo desse erro: http://stackoverflow.com/questions/22674950/python-multiprocessing-job-to-celery-task-but-attributeerror.

Este problema deve ser reaberto ...

Acabei de ter o mesmo problema. Estou usando nltk dentro de um dos meus workers que por sua vez importa scikit-learn que leva ao mesmo erro que @ostrokach mostrou.

Parece que consigo resolver isso com o seguinte código:

from celery.signals import worker_process_init

@worker_process_init.connect
def fix_multiprocessing(**kwargs):
    from multiprocessing import current_process
    try:
        current_process()._config
    except AttributeError:
        current_process()._config = {'semprefix': '/mp'}

Este é obviamente um hack muito bruto e eu não sei o que aconteceria se eu realmente usasse o multiprocessamento (diabos, eu nem sei o que semprefix é), mas é o suficiente para fazer scikit-learn trabalhe novamente.

Estou deixando isso aqui para outras pessoas que tropeçam no mesmo problema até que ele seja corrigido.

Isso poderia ser algum tipo de incompatibilidade com o bilhar no Python 3? Ou também é reproduzível no Python 2?

O problema original de processos de aipo não serem capazes de criar subprocessos ainda é um problema? Revendo os comentários, o problema foi corrigido com celery /

@martinth Obrigado, este hack funciona para mim também!

@xiaods Obrigado! Sua solução funciona para mim! Obrigado!

@gilinson ainda é um problema e exportar PYTHONOPTIMIZE = 1 ainda "meio que corrige".
Acabei de encontrar o mesmo problema ao tentar executar o manual do ansible na tarefa do Celery

@martinth Obrigado pelo hack! Estou tendo o mesmo problema com:

  • Python 3.4.3
  • aipo == 3.1.18
  • scikit-learn == 0,17

O hack de @martinth não funciona para mim, encontrei este tentando usar multiprocessamento para acelerar alguns cálculos. Em vez disso, usei o módulo de encadeamento e parece aliviar esse erro para mim enquanto ainda interrompe meu processamento.

Usar o multiprocessing.dummy baseado em thread funcionou no aipo para mim:

from multiprocessing.dummy import Pool

Este erro ainda ocorre no python 2.7.5 também. Não tenho certeza se a intenção é resolvê-lo, mas isso torna o uso do salt-ssh do saltstack inutilizável com aipo.

Fechando isso, pois não temos os recursos para concluir esta tarefa.

Solução possível"

Eu tinha uma tarefa que estava tentando criar threads e isso iria falhar. Consegui fazê-lo funcionar: bifurcando para um script bash que se bifurca para um interpretador Python que faz exatamente o mesmo código (e, portanto, poderia criar threads, o que era crítico para o meu caso de uso).

Não entendo por que o tíquete está fechado. Se você não tiver recursos para isso, pode fazer um comentário sobre isso, mas isso não fecha o tíquete. Você está apenas escondendo o bug ao fazer isso.

O que é especialmente ruim para um tíquete rotulado como "crítico", tanto para prioridade quanto para gravidade.

@orzel +1.
Prioridade: Crítica
Gravidade: crítica
Fechando isso, pois não temos os recursos para concluir esta tarefa.

Isso é uma piada. Se você não tem recursos agora - então não conserte agora. Corrija quando você tiver recursos. Fechar o tíquete não removerá o problema

@orzel @Templarrr Eu @ask não é o culpado aqui.
Você pode estar infeliz com isso, mas protestar não ajudará.
Precisamos preparar nosso backlog com base no que é acionável e o que não é e atualmente não é.
É uma decisão difícil de fazer, mas alguém tem que fazer isso.
Se esse problema estiver no seu caminho, tente corrigi-lo. Prometo que se a correção estiver correta e tiver os testes apropriados, irei mesclá-la.

@thedrow
Você entendeu mal. Em dobro.
Nossa preocupação não é que você não tenha o recurso (isso é perfeitamente compreensível e, infelizmente, um caso muito comum em software livre). Nossa preocupação é que o ticket esteja fechado por causa disso, não é assim que funciona o ticket.
Não somos 'infelizes', estamos chocados.

Eu também discordo totalmente de encerrar isso.

Acho que todos podemos concordar que isso _é_ realmente um bug. E embora seja realmente triste que não haja recursos suficientes, o fechamento de um bug _definito_ não ajudará nisso. Você não pode saber se talvez amanhã alguém venha e pense "vamos consertar alguns bugs no aipo" apenas para olhar os problemas em aberto e pense "Bem, não há nenhum trabalho interessante a fazer aqui ... vamos trabalhar em _OtherProject_ em vez ".
Além disso, encerrar esse problema tornará mais difícil encontrá-lo. Não sei como você usa o Github, mas quando descubro um problema em potencial, primeiro procuro no rastreador de problemas por problema aberto. Normalmente há muita discussão e mais do que nunca, também há uma solução alternativa (como neste caso) que posso usar por agora. Só se eu ficar _realmente_ desesperado, começo a examinar as questões fechadas.

Isso não é "manipulação de pendências", é ajuste de número. Se eu olhar as coisas para usar, _do_ vejo a contagem de problemas em aberto, mas também sempre olho a contagem de estrelas (que é muito alta para o aipo). Eu entendo que é desejável ter uma contagem de bugs baixa para apelação pública, mas também para seu próprio bem. Honestamente, eu entendo que ver "250 questões em aberto" não é um bom número e parece esmagador.

Se você não tem mão de obra para trabalhar nisso no próximo mês (ou mesmo ano) _está tudo bem_. Só não feche. O fechamento só deve acontecer se o problema estiver concluído ou estiver _absolutamente claro_ que _nunca_ será feito. Ambos não são o caso aqui.

Apenas remova os sinalizadores "Crítico" e adicione um sinalizador "Adiado" para qualquer coisa que não possa ser tratada agora, mas deve ser tratada se o recurso _estiver_ disponível.

Não tenho certeza se podemos corrigir o problema. Não podemos mudar a forma como o Unix funciona, mas podemos enviar um patch upstream para remover a restrição?

Talvez existam soluções específicas para plataformas no Linux, mas isso teria que ser pesquisado. Está aberto há 2 anos sem que ninguém tenha o incentivo para consertá-lo, então é improvável que seja consertado no próximo recurso.

Fechei mais de 200 questões e marquei mais de 30 mil e-mails como lidos, portanto, alguns deles podem ser controversos e talvez tenhamos que reabri-los. Eu espero completamente isso, mas seria bom se também pudéssemos contribuir para uma solução, por exemplo, ajudando a documentar o defeito se essa for a única opção conhecida.

Estamos atolados de trabalho, tentando operar um grande projeto sem recursos. Não podemos fazer a triagem de problemas ou descobrir quais problemas já foram corrigidos.

Bem ... tudo bem. Mas poderia o fato de que "Você não pode usar multiprocessamento se escrever código para um trabalhador de aipo" pelo menos ser documentado? Quer dizer ... sempre haverá pessoas que não leem, mas pelo menos você pode apontar para ele e dizer "Veja, está documentado. Não podemos mudar isso. Lide com isso."

Minha lista de tarefas é gigantesca, você pode editar a documentação diretamente no github agora, então é realmente fácil contribuir com mudanças como estas :(

Não faço isso para esconder problemas, estou fazendo isso para levar as pessoas à ação, precisamente porque quero ver isso melhorado.

@ask Podemos aplicar multiprocessamento dentro da tarefa usando aipo no django?
Existe alguma alternativa para fazer isso?

@abhisheksachan você deve ler toda esta edição antes de postar tal questão

@abhisheksachan Não tento fazer isso há alguns anos, mas consegui fazer funcionar usando https://pypi.python.org/pypi/billiard porque permite daemonização de subprocessos.

Sim, você tem que substituir as importações de 'multiprocessamento' por 'bilhar', por exemplo:

from multiprocessing import Process

->

from billiard import Process

Não há como desabilitar a limitação de multiprocessamento, mas argumentamos que não deveria haver uma limitação de qualquer maneira, então nosso fork de multiprocessamento permite isso.

Para qualquer pessoa que, como eu, investiu no desenvolvimento de um sistema de enfileiramento _BEFORE_ para descobrir essa limitação e precisa de uma solução alternativa diferente até que possa migrar para um wrapper de python rabbitMQ mais utilizável, consegui contornar o problema chamando um subprocesso externo que pode garfo-se bem. Esse processo bifurcado agora está fora da caixa de areia do aipo e as coisas funcionam como deveriam.

No exemplo de OP, substitua:

app = Celery(__name__, broker='amqp://192.168.33.40') 
@app.task
def f():
    manager = multiprocessing.Manager()

com:

app = Celery(__name__, broker='amqp://192.168.33.40')
@app.task
def f():
    process = subprocess.Popen(["program"]) # or the newer post 3.5 run version
    process.wait()
    # analyze exit code

e o "programa" será semelhante (na plataforma POSIX unix / linux)

import os

def main():
      manager = multiprocessing.Manager()

# this is equivalent to "(cmd )&" under bash
pid = os.fork()
if pid == 0:
    cpid = os.fork()
    if cpid == 0:
        main()
    else:
        exit(0)
else:
    os.wait(pid)

Tenha em mente que o gerenciamento da CPU escapa do escopo do aipo, o que vai contra a ideia de usar o aipo, mas como você iria usar o multiprocessamento, provavelmente deseja lidar com o uso da CPU fora do aipo de qualquer maneira.

No mínimo, essa limitação deve ser documentada. Procurei no documento e não consegui encontrar.

Novamente, sinta-se à vontade para enviar uma solicitação pull com as alterações na documentação.

seguindo o comentário de @martinth , no Python 3.5.2, Celery 4.0.0 e billiard 3.5.0 sua solução não funcionou, pois o multiprocessamento verifica o processo que está sendo daemonizado e o impede de iniciar um filho.

Consegui remover a restrição redefinindo o sinalizador do daemon do trabalhador. Tenho certeza de que é uma má ideia, mas permite iniciar multiprocessamento. Pool de dentro de uma tarefa de aipo.

@worker_process_init.connect
def fix_multiprocessing(**kwargs):
    # don't be a daemon, so we can create new subprocesses
    from multiprocessing import current_process
    current_process().daemon = False

Dito isso, o IMHO Celery deve adicionar uma opção documentada para configurar se ele inicia trabalhadores como deamons. Observação Estou usando aipo em um pod K8, portanto, o aipo é iniciado como um processo de primeiro plano usando celery worker , e eu realmente não preciso de trabalhadores daemonizados.

@miraculixx O problema com essa sugestão é que teremos mais modos de falha para lidar e mais problemas para resolver. Preferimos evitar isso.

Embora o uso de multiprocessamento falhe em combinação com o pool prefork, parece funcionar ao usar o pool solo. Portanto, acho que uma solução alternativa seria gerar vários trabalhadores de aipo com o pool solo, em vez de um com vários filhos no pool prefork. Isso soa legítimo? Claro que assim algumas opções como max-mem-per-child não funcionarão.

Acho que isso é basicamente um problema de design do aplicativo. daemonic processes are not allowed to have children é uma dor especial, porque, você sabe, você chegou a um ponto em que precisa redesenhar todo o aplicativo. Mas é uma limitação do nível do sistema operacional, você não pode contorná-la sem efeitos colaterais graves. Os processos demoníacos também não podem ter filhos em C. Isso não é algo específico do Python. Costumava haver um debate sobre o desempenho do thread vs processo e, como conclusão, nenhum deles é significativamente melhor ou pior do que o outro.

Eu sugiro duas opções (e falando em geral, não sobre aipo aqui)

  • Use subprocess.Popen para gerar um processo independente, que pode ter filhos e usar soquetes UNIX para comunicação entre processos
  • É realmente necessário que os threads sejam gerados por um processo bifurcado e não por seu processo principal?

Pelo que vale a pena, meu caso de uso naquela época era que eu queria lançar um subprocesso de longa execução que frequentemente travava rapidamente devido a problemas de entrada não triviais (e não sensíveis à segurança). Então a ideia era pelo menos garantir que o processo fosse iniciado com sucesso.

No longo prazo, acabou sendo um design ruim por vários motivos, então a nova arquitetura naturalmente reverteu para o uso "natural" de trabalhadores assíncronos de aipo. Portanto, concordo com a ideia de questionar se a bifurcação é realmente necessária; a tarefa _é_ a bifurcação.

pelo que vale a pena, meu caso de uso foi lançar processos scikit-learn que usa multiprocessamento (por meio de joblib). Desde então, desenvolvi um back-end de aipo para joblib, o que significa que o scikit-learn inicia processos paralelos usando aipo e meu hack acima não é mais necessário. Isso está em um estágio POC, não está pronto para o horário nobre ainda.

@miraculixx Você tem isso hospedado em algum lugar? Eu gostaria de dar uma olhada e / ou experimentar. Estou tendo o mesmo problema que você - sklearn spawning subprocessos - e basicamente desisti do Celery.

@pgeez Se você não se preocupa em usar subprocessos no sklearn, pode definir a variável de ambiente JOBLIB_MULTIPROCESSING = 0. Consulte https://github.com/scikit-learn/scikit-learn/blob/0.18.X/sklearn/externals/joblib/_multiprocessing_helpers.py

@jennaliu obrigado pela ideia, mas como @miraculixx , preciso habilitar o multiprocessamento.

Vocês já tentaram o velho truque do Unix double-fork para rejeitar as crianças do processo daemon do aipo?

Você leu o título deste tópico?!?!?

@sebastroy obviamente, venho acompanhando esse tópico há anos. Acabei de descobrir o garfo duplo , mas agora vejo que minha confusão era pensar que os garfos daemon estavam sendo mortos pelo aipo, não que eles estivessem sendo impedidos de imediato.

Peguei vocês. Sim, na minha vida anterior eu estava usando C, então isso era como pão com manteiga.

A solução que estou usando é subprocess.Popen que funciona bem, mas então você precisa reimplementar alguma lógica (e fazer uma versão shell do programa) que é o que o aipo deveria estar fazendo em primeiro lugar. Mas eu corrigi isso alterando o comportamento da implementação da API de nível superior. É mais alinhado com o propósito do aipo, eu acho. Simplifique algumas lógicas também que o nível baixo.

Felizmente, encontro esse problema quando tento executar o manual do ansible na tarefa de aipo.
O método fornecido por @martinth não funcionou para mim. Eu imprimo current_process()._config e recebo
{'authkey': b"y&e\x8d'\xcb\xd4\r\xd2\x86\x06\xe7\x9e\x14\xaf \xbc\xc4\x95\xa5G\xec&[i\x19\xf3G-\x06\xac\x19", 'semprefix': '/mp', 'daemon': True} .
Então eu reatribuo o campo daemon para False e funciona.

Existem algumas soluções ou outras implementações para permitir a execução de multiprocessos na tarefa?

@HeartUnchange : recentemente estamos trabalhando duro em um projeto de big data, que desejamos usar aipo como o componente distribuído. e com o seu guia, temos a sorte de resolver o problema. veja a configuração da tarefa:

     @app.task
    def handleBigZipfile(filename,nid):
    current_process()._config['daemon'] = False
    logger.info('{} begin handle!'.format(filename))
    handleAll(filename,nid)
     logger.info('{} is done!'.format(filename))

A solução está ok! começamos o projeto em 2017.1 e agora o protótipo está concluído! nove meses se passaram! Eu tenho meus agradecimentos a você! e meu agradecimento é indescritível!
você poderia descrever mais sobre como descobrir o problema! estamos ansiosos para saber disso!

Oi ,

Eu tenho uma configuração bastante padrão: Django + Rabbitmq + celery-4.0.2 + python-2.7 + centOS-7

Estou tentando gerar um processo usando o módulo de multiprocessamento python padrão em aipo.

Os processos daemon não têm permissão para criar processos filho e, como resultado, as tarefas que usam o pacote de multiprocessamento não estão funcionando:
Comando usado para executar: celery worker -B -A celery_task -l debug
Logs de rastreamento:

[2017-09-26 23:27:08,838: WARNING/PoolWorker-2] ERROR
[2017-09-26 23:27:08,839: WARNING/PoolWorker-2] Traceback (most recent call last):
[2017-09-26 23:27:08,839: WARNING/PoolWorker-2] File "/home/induser/config.py", line 612, in main
[2017-09-26 23:27:08,840: WARNING/PoolWorker-2] mylog_process = mp.Process(target=test_logger_process, args=(myqueue,))
[2017-09-26 23:27:08,840: WARNING/PoolWorker-2] File "/usr/lib64/python2.7/multiprocessing/process.py", line 98, in __init__
[2017-09-26 23:27:08,841: WARNING/PoolWorker-2] self._authkey = _current_process._authkey
[2017-09-26 23:27:08,841: WARNING/PoolWorker-2] AttributeError: 'Process' object has no attribute '_authkey'

Qual poderia ser a razão para não gerar o processo?
Aqui está o código:

import multiprocessing as mp
from celery.schedules import crontab
from celery.decorators import periodic_task

@periodic_task(run_every=crontab(minute='*/1'), name='test_process_celery')
def main():
data = config_read()
try:
    myqueue = mp.Queue(-1)
    mylog_process = mp.Process(target=test_logger_process, args=(myqueue,))
    mylog_process.start()
    . . .
    . . .
except Exception as e:
    raise
finally:
    mylog_process.join()

Te agradece.

tente mestre e relate se ainda for o problema

Ainda tem o erro. Tentei usar um subprocesso com:

from multiprocessing import Process, Value
import ctypes

[...]
        result = Value('i', 0)
        text = Value(ctypes.c_char_p, fail_string.encode())
        p = Process(target=reader.find_text_async, args=(result, text, ))
        p.start()
        p.join(5)

        if p.is_alive():
            logging.WARNING("Manual terminating the process 'find_text_async'")
            p.terminate()

mas com o ramo mestre de aipo diz:

Arquivo "/usr/lib/python3.5/multiprocessing/process.py", linha 103, no início
'processos demoníacos não podem ter filhos'
AssertionError: processos daemônicos não podem ter filhos

EDITAR

Mudei multiprocessamento com bilhar e funciona!

from billiard import Process, Value

Esta página foi útil?
0 / 5 - 0 avaliações