【问题标题】:python multiprocessing - select-like on running processes to see which have one have finishedpython multiprocessing - 在正在运行的进程上进行类似选择,以查看哪些已完成
【发布时间】:2016-03-17 19:37:52
【问题描述】:

我想运行 15 个命令,但一次只运行 3 个

testme.py

import multiprocessing
import time
import random
import subprocess

def popen_wrapper(i):
    p = subprocess.Popen( ['echo', 'hi'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout, stderr = p.communicate()
    print stdout
    time.sleep(randomint(5,20)) #pretend it's doing some work
    return p.returncode

num_to_run = 15
max_parallel = 3

running = []
for i in range(num_to_run):
    p = multiprocessing.Process(target=popen_wrapper, args=(i,))
    running.append(p)
    p.start()

    if len(running) >= max_parallel:
        # blocking wait - join on whoever finishes first then continue
    else:
        # nonblocking wait- see if any processes is finished. If so, join the finished processes

我不确定如何在以下位置实现 cmets:

if len(running) >= max_parallel:
    # blocking wait - join on whoever finishes first then continue
else:
    # nonblocking wait- see if any processes is finished. If so, join the finished processes

我将无法执行以下操作:

for p in running:
   p.join()

因为正在运行的第二个进程已经完成,但我仍然阻塞在第一个进程上。

问题:如何检查running 中的进程是否在阻塞和非阻塞状态下都已完成(找到第一个已完成)?

寻找类似于waitpid的东西,也许

【问题讨论】:

  • 您能否创建一个成功完成的流程以通过队列向您的观看流程发送消息?

标签: python multiprocessing waitpid


【解决方案1】:

也许最简单的安排方法是使用multiprocessing.Pool

pool =  mp.Pool(3)

将设置一个包含 3 个工作进程的池。然后你可以向池中发送 15 个任务:

for i in range(num_to_run):
    pool.apply_async(popen_wrapper, args=(i,), callback=log_result)

以及协调 3 名工人和 15 项任务所需的所有机器 由mp.Pool 照顾。


使用 mp.Pool

import multiprocessing as mp
import time
import random
import subprocess
import logging
logger = mp.log_to_stderr(logging.WARN)

def popen_wrapper(i):
    logger.warn('echo "hi"')
    return i

def log_result(retval):
    results.append(retval)

if __name__ == '__main__':

    num_to_run = 15
    max_parallel = 3
    results = []

    pool =  mp.Pool(max_parallel)
    for i in range(num_to_run):
        pool.apply_async(popen_wrapper, args=(i,), callback=log_result)
    pool.close()
    pool.join()

    logger.warn(results)

产量

[WARNING/PoolWorker-1] echo "hi"
[WARNING/PoolWorker-3] echo "hi"
[WARNING/PoolWorker-1] echo "hi"
[WARNING/PoolWorker-1] echo "hi"
[WARNING/PoolWorker-3] echo "hi"
[WARNING/PoolWorker-1] echo "hi"
[WARNING/PoolWorker-3] echo "hi"
[WARNING/PoolWorker-1] echo "hi"
[WARNING/PoolWorker-3] echo "hi"
[WARNING/PoolWorker-1] echo "hi"
[WARNING/PoolWorker-3] echo "hi"
[WARNING/PoolWorker-1] echo "hi"
[WARNING/PoolWorker-1] echo "hi"
[WARNING/PoolWorker-3] echo "hi"
[WARNING/PoolWorker-2] echo "hi"
[WARNING/MainProcess] [0, 2, 3, 5, 4, 6, 7, 8, 9, 10, 11, 12, 14, 13, 1]

日志语句显示哪个 PoolWorker 处理每个任务,最后一个日志语句显示 MainProcess 已收到来自对 popen_wrapper 的 15 次调用的返回值。


如果您想在没有池的情况下执行此操作,您可以为任务设置 mp.Queue,为返回值设置 mp.Queue

使用mp.Processmp.Queues

import multiprocessing as mp
import time
import random
import subprocess
import logging
logger = mp.log_to_stderr(logging.WARN)

SENTINEL = None
def popen_wrapper(inqueue, outqueue):
    for i in iter(inqueue.get, SENTINEL):
        logger.warn('echo "hi"')
        outqueue.put(i)

if __name__ == '__main__':

    num_to_run = 15
    max_parallel = 3

    inqueue = mp.Queue()
    outqueue = mp.Queue()
    procs = [mp.Process(target=popen_wrapper, args=(inqueue, outqueue)) 
             for i in range(max_parallel)]

    for p in procs:
        p.start()
    for i in range(num_to_run):
        inqueue.put(i)
    for i in range(max_parallel):
        # Put sentinels in the queue to tell `popen_wrapper` to quit
        inqueue.put(SENTINEL)

    for p in procs:
        p.join()

    results = [outqueue.get() for i in range(num_to_run)]
    logger.warn(results)

注意,如果你使用

procs = [mp.Process(target=popen_wrapper, args=(inqueue, outqueue)) 
         for i in range(max_parallel)]

然后你强制执行 max_parallel(例如 3 个)工作进程。然后将所有 15 个任务发送到一个队列:

for i in range(num_to_run):
    inqueue.put(i)

并让工作进程从队列中拉出任务:

def popen_wrapper(inqueue, outqueue):
    for i in iter(inqueue.get, SENTINEL):
        logger.warn('echo "hi"')
        outqueue.put(i)

您还可以找到感兴趣的Doug Hellman's multiprocessing tutorial。在许多指导性示例中,您会发现an ActivePool recipe,它展示了如何生成 10 个进程并限制它们(使用mp.Semaphore),以便在任何给定时间只有 3 个处于活动状态。虽然这可能具有指导意义,但在您的情况下它可能不是最佳解决方案,因为您似乎没有理由要生成 3 个以上的进程。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-05-04
    • 2016-08-16
    • 1970-01-01
    • 2017-10-20
    • 1970-01-01
    • 1970-01-01
    • 2017-10-20
    • 1970-01-01
    相关资源
    最近更新 更多