多处理队列-子进程有时会卡住并且不会收获

时间:2019-02-04 12:10:20

标签: python multiprocessing

首先,我对标题有点怪异表示歉意,但我真的没想到如何将我面临的问题放在一行中。

所以我有以下代码

import time
from multiprocessing import Process, current_process, Manager
from multiprocessing import JoinableQueue as Queue

# from threading import Thread, current_thread
# from queue import Queue


def checker(q):
    count = 0
    while True:
        if not q.empty():
            data = q.get()
            # print(f'{data} fetched by {current_process().name}')
            # print(f'{data} fetched by {current_thread().name}')
            q.task_done()
            count += 1
        else:
            print('Queue is empty now')
            print(current_process().name, '-----', count)
            # print(current_thread().name, '-----', count)


if __name__ == '__main__':
    t = time.time()
    # m = Manager()
    q = Queue()
    # with open("/tmp/c.txt") as ifile:
    #     for line in ifile:
    #         q.put((line.strip()))
    for i in range(1000):
        q.put(i)
    time.sleep(0.1)
    procs = []
    for _ in range(2):
        p = Process(target=checker, args=(q,), daemon=True)
        # p = Thread(target=checker, args=(q,))
        p.start()
        procs.append(p)
    q.join()
    for p in procs:
        p.join()

样本输出

1:进程挂起时

Queue is empty now
Process-2 ----- 501
output hangs at this point

2:一切正常时。

Queue is empty now
Process-1 ----- 515
Queue is empty now
Process-2 ----- 485

Process finished with exit code 0

现在,该行为是间歇性的,有时但并非总是如此。

我也尝试使用Manager.Queue()来代替multiprocessing.Queue(),但没有成功,而且都出现相同的问题。

我使用multiprocessingmultithreading进行了测试,并且得到了完全相同的行为,与{{1}相比,multithreading的行为发生率略有不同}}。

因此,我认为我在概念上缺少某些东西或做错了,但是由于我花了太多时间在此上,现在我无法抓住它,现在我的想法是看不到一些非常基本的东西。

因此,感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

我相信您在checker方法中存在竞争条件。您检查队列是否为空,然后以 separate 步骤使下一个任务出队。在没有互斥或锁定的情况下,将这两种操作分开通常不是一个好主意,因为队列的状态可能在检查和弹出之间改变。它可能是非空的,但另一个进程可能会在通过检查的进程能够这样做之前使等待的工作出队。

但是,我通常更喜欢通讯而不是锁定。它不易出错,使意图更清晰。在这种情况下,我将向工作进程(例如None)发送一个哨兵值,以指示所有工作都已完成。然后,每个工作程序仅使下一个对象(始终是线程安全的)出队,并且,如果对象为None,则子进程退出。

下面的示例代码是您程序的简化版本,应该可以正常运行:

def checker(q):
    while True:
        data = q.get()
        if data is None:
            print(f'process f{current_process().name} ending')
            return
        else:
            pass # do work

if __name__ == '__main__':
    q = Queue()
    for i in range(1000):
        q.put(i)
    procs = []
    for _ in range(2):
        q.put(None) # Sentinel value
        p = Process(target=checker, args=(q,), daemon=True)
        p.start()
        procs.append(p)
    for proc in procs:
        proc.join()