首页 > 解决方案 > multiprocessing_generator 模块触发权限错误

问题描述

我找到了模块multiprocessing_generator。我使用以下代码尝试了该模块:

from multiprocessing_generator import ParallelGenerator

def my_generator():
    yield (x*x for x in range(200))

with ParallelGenerator(my_generator(), max_lookahead=100) as g:
    for elem in g:
        print(elem)

这是我得到的错误(我在控制台中运行了我的代码,python 文件在我的桌面上):

C:\Users\crd\Desktop>python test.py Traceback(最近一次调用最后):文件“test.py”,第 69 行,与 ParallelGenerator(my_generator(), max_lookahead=100) 为 g:文件“C: \Users\crd\AppData\Local\Programs\Python\Python37-32\lib\site-packages\multiprocessing_generator__init__.py",第 62 行,输入 self.process.start() 文件“C:\Users\crd\AppData\Local\Programs\Python\Python37-32\lib\multiprocessing\process.py”,第 112 行,在 start self._popen = self._Popen( self) 文件“C:\Users\crd\AppData\Local\Programs\Python\Python37-32\lib\multiprocessing\context.py”,第 223 行,在 _Popen 返回 _default_context.get_context().Process._Popen(process_obj)文件“C:\Users\crd\AppData\Local\Programs\Python\Python37-32\lib\multiprocessing\context.py”,第 322 行,_Popen return Popen(process_obj) 文件“C:\Users\crd\AppData \Local\Programs\Python\Python37-32\lib\multiprocessing\popen_spawn_win32.py",第 65 行,在init reduction.dump(process_obj, to_child) 文件“C:\Users\crd\AppData\Local\Programs\Python\Python37-32\lib\multiprocessing\reduction.py”,第 60 行,在转储 ForkingPickler(文件,协议)中。转储(obj)AttributeError:无法腌制本地对象'ParallelGenerator。初始化..wrapped'

C:\Users\crd\Desktop>Traceback(最近一次调用最后):文件“”,第 1 行,在文件“C:\Users\crd\AppData\Local\Programs\Python\Python37-32\lib\multiprocessing\ spawn.py”,第 99 行,在 spawn_main new_handle = reduction.steal_handle(parent_pid, pipe_handle) 文件“C:\Users\crd\AppData\Local\Programs\Python\Python37-32\lib\multiprocessing\reduction.py”中,第 87 行,steal_handle _winapi.DUPLICATE_SAME_ACCESS | _winapi.DUPLICATE_CLOSE_SOURCE) PermissionError: [WinError 5] Accès refusé

我从 multiprocessing_generator 模块中清理了代码(我删除了每个尝试/异常,我将上下文管理器转换为基本函数)。但是使用下面的代码我得到了同样的错误:

from queue import Empty

from multiprocessing import Process, Queue

def ParallelGeneratorLight():
    queue = Queue()

    def wrapped():
        for item in (x*x for x in range(200)):
            queue.put(item)

    process = Process(target=wrapped)

    process.start()
    queue.get()

print(ParallelGeneratorLight())

那有什么问题?

标签: pythonpython-3.xmultiprocessinggenerator

解决方案


multiprocessing_generator依赖于使用'fork'start 方法的多处理模块,因为它假定wrapped嵌套函数(在ParallelGenerator's中定义__init__)可以作为target对象传递multiprocessing.Process。在基于forking 的平台上,这很好;子进程继承父进程的完整状态(有一些小的例外,如线程),因此它对wrapped嵌套函数具有平等的访问权限(毕竟它继承了一个精确的副本)。

问题是,在 Windows 上,唯一可用的 start 方法是'spawn',它要求target(和所有参数)pickle能够(它pickle是它们,通过 IPC 将它们发送给孩子,并在那里重建它们),并且嵌套函数永远pickle不能(pickleing 一个函数涉及将其限定名称酸洗以在另一侧导入和使用,并且嵌套函数的限定名称涉及不可导入的组件,在这种情况下,pickleingParallelGenerator.__init__.<locals>.wrapped失败,因为<locals>显然不是可导入的名称)。

基本上,multiprocessing_generator仅适用于类 UNIX 系统,并且仅当您使用默认启动方法 ( 'fork') 时;如果您使用其他值(或)调用set_start_method,则无法正常工作。'forkserver''spawn'multiprocessing_generator

虽然这是一个错误,但在大多数情况下它并不是一个特别严重的错误;如果生成器必须将其值腌制,则模块几乎没有什么好处,因为大多数此类生成器要么pickle不能自己(例如大多数类似文件的对象),要么pickle涉及运行它们直到完成(在这种情况下,您已经失去了所有的并行性)。

抱歉,这里的简单答案是:不要multiprocessing_generator在 Windows 上使用。

也就是说,如果您的生成器是 I/O 绑定的,您可能可以通过导入模块从模块中受益,然后立即对其进行猴子修补,以将multiprocessing其依赖的所有组件替换为其等效multiprocessing.dummy名称(由线程支持,并且不要依赖酸洗),例如

from multiprocessing_generator import ParallelGenerator
import multiprocessing.dummy, multiprocessing_generator

# Monkey-patch to use threads
multiprocessing_generator.Process = multiprocessing.dummy.Process
multiprocessing_generator.Queue = multiprocessing.dummy.Queue

需要明确的是,我没有对此进行测试;没有任何明示或暗示的保证,这是否会起作用。如果生成器受 CPU 限制,至少在 CPython 参考解释器上,这也是完全没有意义的,因为受 CPU 限制的生成器将在运行时持有GIL,从而阻止主线程执行工作,因此您不妨直接迭代.


推荐阅读