退出多处理脚本

时间:2016-08-31 11:58:38

标签: python multiprocessing python-3.5

当我尝试在目标函数抛出错误时退出多处理脚本,但父进程只是挂起而不是退出。

这是我用来复制问题的测试脚本:

#!/usr/bin/python3.5

import time, multiprocessing as mp

def myWait(wait, resultQueue):
    startedAt = time.strftime("%H:%M:%S", time.localtime())
    time.sleep(wait)
    endedAt = time.strftime("%H:%M:%S", time.localtime())
    name = mp.current_process().name
    resultQueue.put((name, wait, startedAt, endedAt))

# queue initialisation
resultQueue = mp.Queue()

# process creation arg: (process number, sleep time, queue)
proc =  [
    mp.Process(target=myWait, name = ' _One_', args=(2, resultQueue,)),
    mp.Process(target=myWait, name = ' _Two_', args=(2, resultQueue,))
    ]

# starting processes
for p in proc:
    p.start()

for p in proc:
    p.join()

# print results
results = {}
for p in proc:
    name, wait, startedAt, endedAt = resultQueue.get()
    print('Process %s started at %s wait %s ended at %s' % (name, startedAt, wait, endedAt))

这很好用,我可以看到父脚本在htop中产生了两个子进程,但是当我想强制父脚本在myWait目标函数中抛出错误时退出进程只是挂起,甚至不会产生任何子进程。我必须ctrl-c来杀死它。

def myWait(wait, resultQueue):
    try:
        # do something wrong
    except:
        raise SystemExit

我已尝试各种方式退出该功能(例如exit()sys.exit()os._exit() ...)无济于事。

2 个答案:

答案 0 :(得分:2)

首先,您的代码存在一个主要问题:您在刷新队列内容(如果有)之前尝试加入进程,这可能会导致死锁。请参阅标题为'加入使用队列的流程'在这里:https://docs.python.org/3/library/multiprocessing.html#multiprocessing-programming

其次,对resultQueue.get()的调用将阻塞,直到它收到一些从未发生过的数据 如果从myWait函数引发异常,并且在此之前没有数据被推入队列。因此,将其设置为非阻塞,并使其检查循环中的任何数据,直到它最终收到某些内容或某些错误。

这是一个快速的解决方案,可以提供您的想法:

#!/usr/bin/python3.5

import multiprocessing as mp
import queue
import time

def myWait(wait, resultQueue):
    raise Exception("error!")

# queue initialisation
resultQueue = mp.Queue()

# process creation arg: (process number, sleep time, queue)
proc =  [
    mp.Process(target=myWait, name = ' _One_', args=(2, resultQueue,)),
    mp.Process(target=myWait, name = ' _Two_', args=(2, resultQueue,))
    ]

# starting processes
for p in proc:
    p.start()

# print results
results = {}
for p in proc:
    while True:
        if not p.is_alive():
            break

        try:
            name, wait, startedAt, endedAt = resultQueue.get(block=False)
            print('Process %s started at %s wait %s ended at %s'
                  % (name, startedAt, wait, endedAt))
            break
        except queue.Empty:
            pass

for p in proc:
    p.join()

函数myWait将抛出异常,但两个进程仍然会加入,程序将很好地退出。

答案 1 :(得分:1)

您应该使用multiprocessing.Pool来管理您的流程。然后使用Pool.imap_unordered按结果顺序迭代结果。一旦获得第一个异常,就可以停止池及其子进程(退出with Pool() as pool块时会自动完成)。例如

from multiprocessing import Pool
import time

def my_wait(args):
    name, wait = args
    if wait == 2:
        raise ValueError("error!")
    else:
        startedAt = time.strftime("%H:%M:%S", time.localtime())
        time.sleep(wait)
        endedAt = time.strftime("%H:%M:%S", time.localtime())
        return name, wait, startedAt, endedAt

if __name__ == "__main__":
    try:
        with Pool() as pool:
            args = [["_One_", 2], ["_Two_", 3]]
            for name, wait, startedAt, endedAt in pool.imap_unordered(my_wait, args):     
                print('Task %s started at %s wait %s ended at %s' % (name,
                    startedAt, wait, endedAt))
    except ValueError as e:
        print(e)

此方法不适用于长时间,低工作负载的任务,因为它只会像管理的子进程数一样并行运行许多任务(但这是您可以设置的)。如果你需要运行不同的功能,这也不是很好。