从廖雪峰教程中运行分布式进程 的例子,在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依然报错
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.')