我在一个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相同的输出,但显然带有我不想在实际脚本中使用的参数。
这是怎么回事?
答案 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
电话中进行注册。