os.scandir和多处理-ThreadPool有效,但多进程池不起作用

时间:2019-09-09 07:53:47

标签: python python-3.x multiprocessing python-multiprocessing scandir

我在一个Python脚本中有一个任务,该任务以前通常是IO绑定的,所以我使用了ThreadPools,并且一切正常。现在我的任务变得越来越受CPU限制,所以我想切换到具有多个进程的Pools。

我认为这两个接口的行为几乎完全相同,因此我刚刚切换了导入,应该一切顺利。但是,突然我的worker函数不再在池中执行了。

尝试了几件事之后,这似乎与我将os.scandir()的DirEntry传递给我的辅助函数的事实相对应。用硬编码字符串替换“ entry”,执行我的worker函数。将其替换为入口,它将停止工作。用ThreadPool代替导入,它可以再次工作。

# This works.
from multiprocessing.pool import ThreadPool as Pool
import os

pool_size = 3

def worker(entry):
    print("Did some useful stuff!")

pool = Pool(pool_size)

for entry in os.scandir("Samples/"):
    if entry.is_file():
        pool.apply_async(worker, (entry,))

pool.close()
pool.join()

print("Finished multiprocessing task.")

输出:

Did some useful stuff! (~150x)
Finished multiprocessing task.

from multiprocessing.pool import ThreadPool as Pool替换为from multiprocessing import Pool,我现在得到的唯一输出是:

Finished multiprocessing task.

现在,如果我将随机字符串而不是循环中的条目插入pool.apply_async(worker, (entry,))中,例如pool.apply_async(worker, ("Why does this work?",)),辅助函数可以工作并返回与ThreadPools相同的输出,但显然带有我不想在实际脚本中使用的参数。

这是怎么回事?

1 个答案:

答案 0 :(得分:2)

问题是,传递给子进程的内容正在被腌制,不适用于DirEntry产生的scandir。不幸的是,使用apply_async时,您看不到相应的故障。您将使用简单的apply,这就是我一直跟踪下来的方式,一旦您看到发生了什么,它实际上是有道理的:

TypeError: can't pickle posix.DirEntry objects

根据您的需要,您可以传递entry.path或其他属性(可以腌制,因此,name也可以这样),否则您必须使用其方法的返回值)(DirEntry)添加到您的工作程序中,您的代码应该可以正常运行。


关于了解故障,您可以编写一个小的函数,例如:

def print_failed(caught):
    traceback.print_exc(file=sys.stderr)

并通过添加apply_async在您的error_callback=print_failed电话中进行注册。