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 <module>
    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 <function <lambda> at 0x0000020ED59D3E18>: attribute lookup <lambda> on __main__ failed
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  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端截图:
    linux

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无需改动
运行结果:
result

### 如何使用 Python `multiprocessing.Manager` 模块 #### 创建 Manager 实例并共享列表 为了实现多个进程之间的数据共享,可以通过创建一个 `Manager()` 的实例来获取管理器对象。这个管理器能够提供多种可共享的数据结构,比如列表、字典等。 ```python from multiprocessing import Process, Manager def add_items_to_list(lst): lst.append('item') print(f'List after adding item: {lst}') if __name__ == '__main__': with Manager() as manager: shared_list = manager.list() process = Process(target=add_items_to_list, args=(shared_list,)) process.start() process.join() print(f'Shared list in main process: {list(shared_list)}') ``` 这段代码展示了如何利用 `Manager().list()` 方法创建可以在不同进程中修改的共享列表[^1]。 #### 使用命名空间 (Namespace) 除了基本的数据容器外,还可以借助 `Manager().Namespace()` 来定义更复杂的共享状态: ```python from multiprocessing import Process, Manager def update_namespace(ns): ns.count += 1 print(f'Updated count to {ns.count} inside child process') if __name__ == '__main__': with Manager() as manager: namespace = manager.Namespace() setattr(namespace, 'count', 0) processes = [] for _ in range(3): p = Process(target=update_namespace, args=(namespace,)) p.start() processes.append(p) for p in processes: p.join() print(f'Final value of count is {namespace.count}') ``` 这里说明了怎样通过设置属性的方式向命名空间添加变量,并让它们在各个子进程中保持同步更新[^2]。 #### 锁机制防止竞态条件 当涉及到并发操作时,为了避免可能发生的竞争状况,应该引入锁来进行保护。下面的例子显示了如何结合 `Lock` 和 `Manager` 来确保线程安全的操作: ```python import time from multiprocessing import Process, Manager def safe_increment(counter, lock): with lock: current_value = counter.value time.sleep(0.1) # Simulate some processing delay counter.value = current_value + 1 print(f'Counter incremented safely by a worker.') if __name__ == '__main__': with Manager() as manager: counter = manager.Value('i', 0) lock = manager.Lock() workers = [Process(target=safe_increment, args=(counter, lock)) for i in range(5)] for w in workers: w.start() for w in workers: w.join() print(f'The final counter value should be 5 and it is actually {counter.value}.') ``` 上述程序片段解释了如何运用 `Manager().Value()` 和 `Manager().Lock()` 来维护计数器的安全递增过程[^4].
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值