在python中实现“竞争”流程

时间:2019-12-21 06:31:24

标签: python multiprocessing

我正在尝试实现一个以2个函数作为参数,同时运行两个函数,返回该函数的值的函数,该函数首先返回并杀死较慢的函数,然后再执行它。 我的问题是,当我尝试清空用于收集返回值的Queue对象时,我陷入了困境。 有没有更“正确”的方式来处理这种情况,甚至现有的模块?如果没有,谁能解释我在做什么错? 这是我的代码(上面函数的实现是'run_both()'):

import multiprocessing as mp
from time import sleep


Q = mp.Queue()

def dump_queue(queue):
    result = []
    for i in iter(queue.get, 'STOP'):
        result.append(i)
    return result

def rabbit(x):
    sleep(10)
    Q.put(x)

def turtle(x):
    sleep(30)
    Q.put(x)

def run_both(a,b):
    a.start()
    b.start()
    while a.is_alive() and b.is_alive():
            sleep(1)
    if a.is_alive():
            a.terminate()
    else:
            b.terminate()
    a.join()
    b.join()
    return dump_queue(Q)


p1 = mp.Process(target=rabbit, args=(1,))
p1 = mp.Process(target=turtle, args=(2,))
run_both(p1, p2)

2 个答案:

答案 0 :(得分:0)

下面是使用multiprocessing调用2个或更多函数并返回最快结果的示例。但是,有几件事要注意。

  1. 在IDLE中运行multiprocessing代码有时会引起问题。这个示例有效,但我确实遇到了尝试解决此问题的问题。
  2. 多处理代码应从if __name__ == '__main__'子句内部开始,否则,如果主模块被另一个进程重新导入,它将再次运行。阅读多处理文档页面以获取更多信息。
  3. 结果队列直接传递给使用它的每个进程。当您通过引用模块中的全局名称使用队列时,代码将在Windows上失败,因为每个进程都使用队列的新实例。在这里Multiprocessing Queue.get() hangs
  4. 了解更多

我还在此处添加了一些功能,以了解实际使用了哪个过程的结果。

import multiprocessing as mp
import time
import random

def task(value):
    # our dummy task is to sleep for a random amount of time and
    # return the given arg value
    time.sleep(random.random())
    return value

def process(q, idx, fn, args):
    # simply call function fn with args, and push its result in the queue with its index
    q.put([fn(*args), idx])

def fastest(calls):
    queue = mp.Queue()
    # we must pass the queue directly to each process that may use it
    # or else on Windows, each process will have its own copy of the queue
    # making it useless

    procs = []
    # create a 'mp.Process' that calls our 'process' for each call and start it
    for idx, call in enumerate(calls):
        fn = call[0]
        args = call[1:]
        p = mp.Process(target=process, args=(queue, idx, fn, args))
        procs.append(p)
        p.start()
    # wait for the queue to have something
    result, idx = queue.get()
    for proc in procs: # kill all processes that may still be running
        proc.terminate()
        # proc may be using queue, so queue may be corrupted.
        # https://docs.python.org/3.8/library/multiprocessing.html?highlight=queue#multiprocessing.Process.terminate
        # we no longer need queue though so this is fine
    return result, idx

if __name__ == '__main__':

    from datetime import datetime

    start = datetime.now()
    print(start)
    # to be compatible with 'fastest', each call is a list with the first
    # element being callable, followed by args to be passed
    calls = [
        [task, 1],
        [task, 'hello'],
        [task, [1,2,3]]
        ]
    val, idx = fastest(calls)
    end = datetime.now()
    print(end)
    print('elapsed time:', end-start)
    print('returned value:', val)
    print('from call at index', idx)

示例输出:

2019-12-21 04:01:09.525575
2019-12-21 04:01:10.171891
elapsed time: 0:00:00.646316
returned value: hello
from call at index 1

答案 1 :(得分:0)

倒数第二行上的错字应显示为:

p2 = mp.Process(target=turtle, args=(2,))       # not p1

要使程序正常运行,最简单的更改就是添加:

Q.put('STOP')

turtle()rabbit()的末尾。


根据定义,如果您只是阅读消息队列并收到STOP(其中之一已完成),那么您实际上也不需要一直循环监视进程是否处于活动状态,因此可以替换{{1 }}:

run_both()

您可能还需要考虑如果两个进程几乎同时将一些消息放入队列中会发生什么情况。他们可能会混在一起。也许考虑使用2个队列,或者将所有结果合并为一条消息,而不是将def run_both(a,b): a.start() b.start() result = dump_queue(Q) a.terminate() b.terminate() return result

的多个值附加在一起