python使用multiprocessing.Manager出现PermissionError: [WinError 5] 拒绝访问。

从廖雪峰教程中运行分布式进程 的例子,在windows环境运行出现PermissionError: [WinError 5]
错误日志:

Traceback (most recent call last):
  File "C:\Users\52489\Desktop\Python\mstwrk\task_master.py", line 17, in 
    manager.start()
  File "D:\Users\52489\Anaconda3\lib\multiprocessing\managers.py", line 513, in start
    self._process.start()
  File "D:\Users\52489\Anaconda3\lib\multiprocessing\process.py", line 105, in start
    self._popen = self._Popen(self)
  File "D:\Users\52489\Anaconda3\lib\multiprocessing\context.py", line 322, in _Popen
    return Popen(process_obj)
  File "D:\Users\52489\Anaconda3\lib\multiprocessing\popen_spawn_win32.py", line 65, in __init__
    reduction.dump(process_obj, to_child)
  File "D:\Users\52489\Anaconda3\lib\multiprocessing\reduction.py", line 60, in dump
    ForkingPickler(file, protocol).dump(obj)
_pickle.PicklingError: Can't pickle  at 0x0000020ED59D3E18>: attribute lookup  on __main__ failed
Traceback (most recent call last):
  File "", line 1, in 
  File "D:\Users\52489\Anaconda3\lib\multiprocessing\spawn.py", line 99, in spawn_main
    new_handle = reduction.steal_handle(parent_pid, pipe_handle)
  File "D:\Users\52489\Anaconda3\lib\multiprocessing\reduction.py", line 87, in steal_handle
    _winapi.DUPLICATE_SAME_ACCESS | _winapi.DUPLICATE_CLOSE_SOURCE)
PermissionError: [WinError 5] 拒绝访问。

使用管理员权限运行cmd和powershell依然报错

  1. 将task_master.py放在linux虚拟机中运行,task_worker.py依旧在windows下运行接收task.
    结果正常.
    linux的master端截图:
    python使用multiprocessing.Manager出现PermissionError: [WinError 5] 拒绝访问。_第1张图片

2.问题分析
廖老师教程中在多进程一节的原话

在Unix/Linux下,multiprocessing模块封装了fork()调用,使我们不需要关注fork()的细节。由于Windows没有fork调用,因此,multiprocessing需要“模拟”出fork的效果,父进程所有Python对象都必须通过pickle序列化再传到子进程去,所有,如果multiprocessing在Windows下调用失败了,要先考虑是不是pickle失败了。

显然问题出现在windows平台与linux平台对进程的不同处理.
没想到在这里挖了一个大坑.一个小时的阅读总结一下:
1) linux下创建进程使用fork(),windows平台下创建进程使用CreateProccess().
2) fork()调用之后,没有写操作之前,与父进程共享一份内存,并没有真正拥有所谓多进程的”私有内存”. 而CreateProccess()每次执行之后,都确保子进程拥有新的内存空间.
3) 这样设计的原因可以从linux和windows多进程的应用场景的差别来理解.fork诞生于无线程时代,因此fork()之后不exec的话,作用与多线程非常相似,可以理解为没有线程概念下的一种解决方案.而CreateProccess则更像创建兄弟进程, 创建的进程都保证独立存在.
3) 这种差别直接体现在multiprocessing模块当中,在不同平台下使用不同的方法创建例子中的manager进程. 在windows下通过将父进程的环境进行序列化存储之后,再传入createProccess当中.
4) 问题在于pickling序列化中对匿名函数的不支持,导致创建进程失败.
5) 因此把匿名函数用函数替代即可解决.

改动点有两个:
1. 取消匿名函数
2. 加入if name == main 判断.
第二点是windows平台对于python多进程实现的要求.主要为了防止循环import

Since Windows has no fork, the multiprocessing module starts a new Python process and imports the calling module. If Process() gets called upon import, then this sets off an infinite succession of new processes (or until your machine runs out of resources). This is the reason for hiding calls to Process() inside

修改后的代码:

#task_master.py
import random, time, queue

from multiprocessing.managers import BaseManager

task_queue = queue.Queue()

result_queue = queue.Queue()

#替代原来的匿名函数
def return_task_queue():
    global task_queue
    return task_queue
#替代原来的匿名函数
def return_result_queue():
    global result_queue
    return result_queue


class QueueManager(BaseManager):
    pass

#加入main判断
if __name__ == '__main__':
    #callable参数指定函数
    QueueManager.register('get_task_queue',callable=return_task_queue)
    #callable参数指定函数
    QueueManager.register('get_result_queue',callable=return_result_queue)

    manager = QueueManager(address=('127.0.0.1',34512), authkey=b'abc')

    manager.start()

    task = manager.get_task_queue()
    result = manager.get_result_queue()

    for i in range(10):
        n = random.randint(0, 10000)
        print('Put task %d...' % n)
        task.put(n)

    print('Try get result...')
    for i in range(10):
        r = result.get(timeout=10)
        print('Result: %s' %r)

    manager.shutdown()
    print('master exit.')

task_worker.py无需改动
运行结果:
python使用multiprocessing.Manager出现PermissionError: [WinError 5] 拒绝访问。_第2张图片

你可能感兴趣的:(python学习笔记)