【问题标题】:Nested parallelism in Python multiprocessingPython多处理中的嵌套并行
【发布时间】:2018-04-20 18:45:46
【问题描述】:

我知道这听起来像是以前有人问过的问题,但是等等,我会解释为什么其他选项不起作用。

我目前正在使用multiprocessing.Pool 在应用程序中实现并行性,并希望对其进行扩展以利用嵌套并行性。将Pool 对象作为参数传递给apply_async 的幼稚方法行不通as noted in other answers,因为Pool 不能被腌制。

这是我的要求:

  1. 我需要某种池来限制并发执行任务的数量。例如。 multiprocess.Pool 用于此目的,但不能传递给其他进程。

  2. 我需要嵌套并行。在我的应用程序中,我需要执行 I/O 以识别嵌套工作是什么,所以我绝对不想从单个线程中执行此操作。我认为这排除了this question 的所有答案。

  3. 需要在标准库中;我无法添加依赖项。这排除了this answer

  4. 我真的很希望它同时与 Python 2 和 3 一起工作。但是,如果可以证明迁移到 Python 3 可以解决我的问题,我会考虑它。

我不需要这个来专门使用多个进程,使用线程就可以了,因为大部分工作是 I/O 或等待子进程完成。

我尝试过使用multiprocessing.dummy,它是相同的接口,但在threading 之上实现。但是,当我尝试调用get() 来检索我的测试结果时,我收到以下错误,所以我认为这已经过时了。

  File "/usr/lib/python2.7/multiprocessing/pool.py", line 567, in get
    raise self._value
ValueError: signal only works in main thread

我知道 Python 3 中的 concurrent.futures 库,但这似乎有一些严重的限制。例如,本节中的第二个示例在我的情况下似乎是一个展示终结者:

https://docs.python.org/3/library/concurrent.futures.html#threadpoolexecutor

我看不出你怎么能避免使用基本上任何直接编写的嵌套并行算法来解决这个问题。因此,即使我愿意使用 Python 3,我认为这也是行不通的。

我不知道标准库中有任何其他可用选项,但我自己没有编写实现。

【问题讨论】:

  • “我需要执行 I/O 以识别嵌套工作是什么”。这是网络 I/O 吗?如果是这样,我想知道 IO + 线程的 eventlet 是否可以工作? eventlet.net/doc/threading.html
  • 在我的例子中是文件 I/O;基本上在顶层,我每个工作单元都有一个文件,并且根据该文件包含的内容,我可以利用进一步的嵌套并行性。但是,如果有任何其他合理的选择,我真的宁愿避免依赖。如果标准库没有提供一种利用嵌套并行性的方法,这似乎是一个相当大的限制,如果它不可能,我会感到惊讶。
  • 啊,好吧...另一个问题:会有多少层嵌套?一个级别的文件 I/O,然后在另一个级别的基础上工作,就是这样?
  • 目前,是的,有两层嵌套。
  • 还有一个......对于每个文件,可能会触发进一步的工作。是否可以在处理完该文件后触发工作,还是应该在此过程中触发更多工作?

标签: python multiprocessing python-multiprocessing


【解决方案1】:

你似乎已经排除了,但我怀疑 https://docs.python.org/3/library/concurrent.futures.html#threadpoolexecutorhttps://docs.python.org/3/library/concurrent.futures.html#processpoolexecutor 可以使用,如果您能够迁移到 Python 3,或者为 Python 2 添加依赖项。

如果在处理该文件之前不必触发每个文件的额外工作,则可以有一个触发所有其他线程的单个协调线程,因此可以防止死锁,如下例所示。

from concurrent.futures import ThreadPoolExecutor
import time

pool = ThreadPoolExecutor(max_workers=3)

def find_work_inputs(dummy_file):
    print("{}: Finding work...".format(dummy_file))
    time.sleep(1)
    work = range(0, dummy_file)
    print("{}: Work is {}".format(dummy_file, work))
    return work

def do_work(dummy_file, work_input):
    print("{}: {}".format(dummy_file, work_input))
    print("{}: Doing work {}...".format(dummy_file, work_input))
    time.sleep(1)
    return work_input * work_input

dummy_files = [1,2,3,4,5]

futures = []
for dummy_file in dummy_files:
    work_inputs = pool.submit(find_work_inputs, dummy_file)
    for work_input in work_inputs.result():
        result = work_input
        futures.append((dummy_file, result, pool.submit(do_work, dummy_file, result)))

for dummy_file, work_input, future in futures:
    print("Result from file:{} input:{} is {}".format(dummy_file, work_input, future.result()))

或者,如果第一级的每个线程都需要自己触发工作,那么额外的工作可能需要在另一个池中以防止死锁(取决于每个未来何时调用result()),如下所示。

from concurrent.futures import ThreadPoolExecutor
import time

find_work_pool = ThreadPoolExecutor(max_workers=3)
do_work_pool = ThreadPoolExecutor(max_workers=3)

def find_work_inputs(dummy_file):
    print("{}: Finding work...".format(dummy_file))
    time.sleep(1)
    work = range(0, dummy_file)
    print("{}: Work is {}".format(dummy_file, work))

    futures = []
    for work_input in work:
        futures.append((dummy_file, work_input, do_work_pool.submit(do_work, dummy_file, work_input)))
    return futures

def do_work(dummy_file, work_input):
    print("{}: {}".format(dummy_file, work_input))
    print("{}: Doing work {}...".format(dummy_file, work_input))
    time.sleep(1)
    return work_input * work_input

dummy_files = [1,2,3,4,5]

futures = []
for dummy_file in dummy_files:
    futures.extend(find_work_pool.submit(find_work_inputs, dummy_file).result())

for dummy_file, work_input, future in futures:
    print("Result from file:{} input:{} is {}".format(dummy_file, work_input, future.result()))

【讨论】:

  • 太糟糕了,Python 没有更简洁的方法来实现真正的嵌套并行,但鉴于现实情况,我认为这个答案很好地展示了你必须做什么。不过公平地说,选项 (1) 也适用于 multiprocess.Pool
猜你喜欢
  • 1970-01-01
  • 2021-04-07
  • 1970-01-01
  • 1970-01-01
  • 2020-04-22
  • 1970-01-01
  • 2020-05-17
  • 1970-01-01
  • 2020-09-19
相关资源
最近更新 更多