Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/325.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python多处理进程以静默方式崩溃_Python_Python 2.7_Parallel Processing_Multiprocessing - Fatal编程技术网

Python多处理进程以静默方式崩溃

Python多处理进程以静默方式崩溃,python,python-2.7,parallel-processing,multiprocessing,Python,Python 2.7,Parallel Processing,Multiprocessing,我正在使用Python 2.7.3。我使用子类multiprocessing.Process对象对一些代码进行了并行化。如果我的子类流程对象中的代码中没有错误,那么一切都可以正常运行。但是,如果我的子类进程对象中的代码中有错误,它们显然会无声地崩溃(没有stacktrace打印到父shell),CPU使用率将降至零。父代码从不崩溃,给人的印象是执行只是挂起。同时,很难找出代码中的错误在哪里,因为没有给出错误在哪里的指示 我在stackoverflow上找不到任何其他关于同一问题的问题 我猜子类化

我正在使用Python 2.7.3。我使用子类
multiprocessing.Process
对象对一些代码进行了并行化。如果我的子类流程对象中的代码中没有错误,那么一切都可以正常运行。但是,如果我的子类进程对象中的代码中有错误,它们显然会无声地崩溃(没有stacktrace打印到父shell),CPU使用率将降至零。父代码从不崩溃,给人的印象是执行只是挂起。同时,很难找出代码中的错误在哪里,因为没有给出错误在哪里的指示

我在stackoverflow上找不到任何其他关于同一问题的问题

我猜子类化的流程对象似乎会悄无声息地崩溃,因为它们无法将错误消息打印到父级的shell中,但我想知道我能做些什么,这样我至少可以更高效地进行调试(这样我的代码的其他用户也可以在遇到问题时告诉我)

EDIT:我的实际代码太复杂了,但是一个小例子是子类流程对象中有一个错误,它是这样的:

from multiprocessing import Process, Queue

class Worker(Process):

    def __init__(self, inputQueue, outputQueue):

        super(Worker, self).__init__()

        self.inputQueue = inputQueue
        self.outputQueue = outputQueue

    def run(self):

        for i in iter(self.inputQueue.get, 'STOP'):

            # (code that does stuff)

            1 / 0 # Dumb error

            # (more code that does stuff)

            self.outputQueue.put(result)

这不是一个答案,只是一个延伸的评论。请运行此程序并告诉我们您得到了什么输出(如果有):

我得到:

% test.py
Process Worker-1:
Traceback (most recent call last):
  File "/usr/lib/python2.7/multiprocessing/process.py", line 258, in _bootstrap
    self.run()
  File "/home/unutbu/pybin/test.py", line 21, in run
    1 / 0 # Dumb error
ZeroDivisionError: integer division or modulo by zero

我很惊讶(如果)您什么都没有得到。

您真正想要的是某种将异常传递给父进程的方法,对吗?然后你可以随心所欲地处理它们

如果使用,这是自动的。如果你使用,它是微不足道的。如果您使用显式的
过程
队列
,您需要做一些工作,但并没有那么多

例如:

def run(self):
    try:
        for i in iter(self.inputQueue.get, 'STOP'):
            # (code that does stuff)
            1 / 0 # Dumb error
            # (more code that does stuff)
            self.outputQueue.put(result)
    except Exception as e:
        self.outputQueue.put(e)
然后,您的呼叫代码就可以像其他任何东西一样从队列中读取
Exception
s。与此相反:

yield outq.pop()
这样做:

result = outq.pop()
if isinstance(result, Exception):
    raise result
yield result
(我不知道您的实际父进程队列读取代码是做什么的,因为您的最小样本只是忽略了队列。但希望这能解释这个想法,即使您的实际代码实际上不是这样工作的。)

这假设您希望中止任何未经处理的异常,该异常将导致
运行。如果要传回异常并继续执行iter中的下一个
i
,只需将
try
移动到
for
中,而不是绕过它

这还假设
异常
s不是有效值。如果这是一个问题,最简单的解决方案就是推送
(结果,异常)
元组:

def run(self):
    try:
        for i in iter(self.inputQueue.get, 'STOP'):
            # (code that does stuff)
            1 / 0 # Dumb error
            # (more code that does stuff)
            self.outputQueue.put((result, None))
    except Exception as e:
        self.outputQueue.put((None, e))
然后,弹出代码执行以下操作:

result, exception = outq.pop()
if exception:
    raise exception
yield result
您可能会注意到,这类似于node.js回调样式,您将
(err,result)
传递给每个回调。是的,这很烦人,而且你会以这种方式把代码弄乱。但是你实际上没有在任何地方使用它,除了在包装器中;所有从队列中获取值或在
run
内部调用的“应用程序级”代码只会看到正常的回报/收益和引发的异常

您可能甚至想考虑将代码< >将来/代码>到代码< >并发.Noest< /Cal> >(或使用该类AS),即使您正在执行作业队列并手动执行。这并不难,它为您提供了一个非常好的API,特别是用于调试的API

最后,值得注意的是,通过执行器/池设计,大多数围绕工作者和队列构建的代码可以变得简单得多,即使您绝对确定每个队列只需要一个工作者。只需废弃所有样板文件,并将
Worker中的循环转换为一个函数。将
方法运行为一个函数(它只
正常返回
s或
提升
s,而不是附加到队列中)。在调用端,再次废弃所有样板文件,只需
submit
map
作业函数及其参数

您的整个示例可以简化为:

def job(i):
    # (code that does stuff)
    1 / 0 # Dumb error
    # (more code that does stuff)
    return result

with concurrent.futures.ProcessPoolExecutor(max_workers=1) as executor:
    results = executor.map(job, range(10))
它会自动正确处理异常


正如您在评论中提到的,异常的回溯不会追溯到子进程;它只适用于手动
raiseresult
调用(或者,如果您使用的是池或执行器,则是池或执行器的内脏)

原因是,
multiprocessing.Queue
构建在
pickle
之上,pickle异常不会pickle它们的回溯。原因是你不能对追踪进行酸洗。原因是回溯中充满了对本地执行上下文的引用,所以要让它们在另一个进程中工作是非常困难的


那么…你能做些什么?不要去寻找一个完全通用的解决方案。相反,想想你真正需要什么。90%的情况下,您需要的是“记录异常,并进行回溯,然后继续”或“将异常打印到
stderr
exit(1)
,就像默认的未处理异常处理程序一样”。对于这两种情况,您根本不需要传递异常;只需在子端格式化它并传递一个字符串即可。如果你确实需要一些更奇特的东西,准确地计算出你需要的东西,并传递足够的信息来手动组合起来。如果您不知道如何格式化回溯和异常,请参阅模块。这很简单。这意味着你根本不需要进入腌菜机。(并不是说用
\uuuu reduce\uuuuu
方法编写一个pickler或holder类很难,但如果不需要,为什么要学习所有这些呢?

我建议这样的方法来显示流程异常

from multiprocessing import Process
import traceback


run_old = Process.run

def run_new(*args, **kwargs):
    try:
        run_old(*args, **kwargs)
    except (KeyboardInterrupt, SystemExit):
        raise
    except:
        traceback.print_exc(file=sys.stdout)

Process.run = run_new

你能发布一个简单的测试用例来说明这个问题吗?@Blender-yes。添加了一些代码。这是Python多处理的一个常见问题。我建议用雷。像这样的异常可以很好地进行开箱即用的传播。如果他在shell中没有得到POSIX的任何信息,我会感到惊讶。但是在Windows上,或者在IDLE或PyDev中,或者如果父进程是GUI
from multiprocessing import Process
import traceback


run_old = Process.run

def run_new(*args, **kwargs):
    try:
        run_old(*args, **kwargs)
    except (KeyboardInterrupt, SystemExit):
        raise
    except:
        traceback.print_exc(file=sys.stdout)

Process.run = run_new