【问题标题】:How can I share variables between processes in Python 3?如何在 Python 3 中的进程之间共享变量?
【发布时间】:2021-10-21 18:16:38
【问题描述】:

我在 Windows 10 上使用 Python 3.9.6 x64,我想知道如何在子进程之间共享变量。

为什么?因为我正在使用请求编写异步多连接可恢复下载器,并且默认情况下请求启用keep-alive,这会留下死请求的挂起连接,并经常导致服务器在恢复时从以前的死连接发送额外字节,我尝试过@ 987654329@、r.connection.close()s.close()s.headers['connection'] = 'close',都没有在使用threading.Thread时解决问题。

但是,我从来没有遇到过这样的情况,即接收到来自前一个进程的额外字节,并且所有没有暂停的下载都成功(也就是说,如果连接没有在下载过程中中断,这种情况经常发生),所以我认为如果相应的进程被杀死,连接肯定会被杀死,所以我正在寻找使用multiprocessing.Process 的解决方案(我知道下载是 I/O 绑定而不是 CPU 绑定等,但是线程没有收到新的 pid) ,但我不知道如何在进程之间共享变量...

具体来说我想分享两个对象:

1,一个mmap对象,存储下载的数据。

2,使用此创建的字典:

self.progress['total'] = total
self.progress['connections'] = num_connections
for i in range(num_connections):
    ...
    self.progress[i] = dict()
    self.progress[i]['start'] = start
    self.progress[i]['position'] = start
    self.progress[i]['end'] = end
    self.progress[i]['count'] = 0
    self.progress[i]['length'] = length
    self.progress[i]['completed'] = False

所有变量都是整数,上面的代码只演示了dict是如何创建的。

我在 Google 上搜索了 python share variable between processes,这次 Google 确实从这个站点找到了相关结果,但是所有答案都是针对 Python 2 的,并且无法在我的机器上运行。

例如这个答案: https://stackoverflow.com/a/17393879/16383578

重新格式化为 Python 3 后,我尝试运行它,然后...

In [1]: import time
   ...: from multiprocessing import Process, Manager, Value
   ...:
   ...: def foo(data, name=''):
   ...:     print(type(data), data.value, name)
   ...:     data.value += 1
   ...:
   ...: if __name__ == "__main__":
   ...:     manager = Manager()
   ...:     x = manager.Value('i', 0)
   ...:     y = Value('i', 0)
   ...:
   ...:     for i in range(5):
   ...:         Process(target=foo, args=(x, 'x')).start()
   ...:         Process(target=foo, args=(y, 'y')).start()
   ...:
   ...:     print('Before waiting: ')
   ...:     print('x = {0}'.format(x.value))
   ...:     print('y = {0}'.format(y.value))
   ...:
   ...:     time.sleep(5.0)
   ...:     print('After waiting: ')
   ...:     print('x = {0}'.format(x.value))
   ...:     print('y = {0}'.format(y.value))
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "c:\program files\python39\lib\multiprocessing\spawn.py", line 116, in spawn_main
    exitcode = _main(fd, parent_sentinel)
  File "c:\program files\python39\lib\multiprocessing\spawn.py", line 126, in _main
    self = reduction.pickle.load(from_parent)
EOFError: Ran out of input
---------------------------------------------------------------------------
PicklingError                             Traceback (most recent call last)
<ipython-input-1-779bd728820e> in <module>
     12
     13     for i in range(5):
---> 14         Process(target=foo, args=(x, 'x')).start()
     15         Process(target=foo, args=(y, 'y')).start()
     16

c:\program files\python39\lib\multiprocessing\process.py in start(self)
    119                'daemonic processes are not allowed to have children'
    120         _cleanup()
--> 121         self._popen = self._Popen(self)
    122         self._sentinel = self._popen.sentinel
    123         # Avoid a refcycle if the target function holds an indirect

c:\program files\python39\lib\multiprocessing\context.py in _Popen(process_obj)
    222     @staticmethod
    223     def _Popen(process_obj):
--> 224         return _default_context.get_context().Process._Popen(process_obj)
    225
    226 class DefaultContext(BaseContext):

c:\program files\python39\lib\multiprocessing\context.py in _Popen(process_obj)
    325         def _Popen(process_obj):
    326             from .popen_spawn_win32 import Popen
--> 327             return Popen(process_obj)
    328
    329     class SpawnContext(BaseContext):

c:\program files\python39\lib\multiprocessing\popen_spawn_win32.py in __init__(self, process_obj)
     91             try:
     92                 reduction.dump(prep_data, to_child)
---> 93                 reduction.dump(process_obj, to_child)
     94             finally:
     95                 set_spawning_popen(None)

c:\program files\python39\lib\multiprocessing\reduction.py in dump(obj, file, protocol)
     58 def dump(obj, file, protocol=None):
     59     '''Replacement for pickle.dump() using ForkingPickler.'''
---> 60     ForkingPickler(file, protocol).dump(obj)
     61
     62 #

PicklingError: Can't pickle <function foo at 0x000001E1AFB4AC10>: attribute lookup foo on __main__ failed

我如何才能真正启动新流程并在它们之间共享变量?


嗯,我不明白,上面的代码是在 IPython shell 中运行的,产生了错误,但是当作为脚本运行时,它可以正常工作而没有错误,据我所知,__name__ 两者都有变量案例是'__main__',为什么它们的行为不同?

pickle 在 Python 解释器中工作吗? pickle 似乎没有: https://docs.python.org/3/library/pickle.html

什么可以腌制和不腌制?

可以腌制以下几种:

None, True, and False

integers, floating point numbers, complex numbers

strings, bytes, bytearrays

tuples, lists, sets, and dictionaries containing only picklable objects

functions defined at the top level of a module (using def, not lambda)

built-in functions defined at the top level of a module

classes that are defined at the top level of a module

instances of such classes whose __dict__ or the result of calling __getstate__() is picklable (see section Pickling Class Instances for details).

然而,在 Python 3.9.6 解释器中的这个错误中,它明确指出“shell 进程会话”(不知道它叫什么)是一个模块:

AttributeError: Can't get attribute 'foo' on <module '__main__' (built-in)>

pickle 在 Python 解释器中工作吗?如果没有,那为什么呢?

我已经尝试在 Google 上搜索此内容,但不出所料地再次找不到任何有用的内容。


不,pickle.dumps()pickle.loads() 工作正常,reduction.pickle 指向 pickle 模块:

>>> from multiprocessing.spawn import reduction
>>> reduction.pickle
<module 'pickle' from 'C:\\Program Files\\Python39\\lib\\pickle.py'>

所以错误确实是AttributeError: Can't get attribute 'foo' on &lt;module '__main__' (built-in)&gt;,所以在脚本中运行时似乎foo__main__的属性,但在shell中运行时不是这样,但为什么呢?

另外,如果我输入 __name__,Python 会返回 '__main__',这是一个 str,但是当我在 IPython 中输入 foo 时,它会显示:

<function __main__.foo(data, name='')>

这似乎表明__main__ 是一个变量,但是当我输入__main__ 时:

In [8]: __main__
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-8-19bc6e7e07cd> in <module>
----> 1 __main__

NameError: name '__main__' is not defined

那么__main__到底是什么?我猜它是当前的全局命名空间,如果是,我如何在 shell 中访问它(它的变量名称是什么)?


似乎多处理在 Python shell 中不起作用,但我无法确认这一点,因为 Google 未能找到任何相关内容...


刚刚将.start() 的两次出现更改为.run() 并且上面的代码神奇地在Python shell 中运行,为什么Process.start()Process.run() 不同?


那么多处理真的不能在 Python shell 中工作吗?


这个东西真的很复杂,我在VMWare Workstation 16 Player上安装了Windows 10 21H1,然后在上面安装了Python 3.9.6 x64只是为了测试代码。

我逐行输入代码,在进程开始时发生了同样的错误(我还不知道如何在主机和客户机之间复制粘贴):

所以要么我非常不走运,要么 Windows x64 的 Python 3.9.6 出了问题......

【问题讨论】:

标签: python python-3.x multiprocessing


【解决方案1】:

我已尽我所能替换了我的环境(Windows、Python v3.9.6),但无法重现您的错误。多处理代码工作正常,能够使用multiprocessing.Managermultiprocessing.Value 在进程之间共享对象。

这个问题通常是因为 - only top-level functions/statements can be pickled.

我建议您尝试pathos.multiprocessing,这是一个使用 dill 代替 pickle 的多处理分支。 dill 几乎可以在 python 中序列化任何东西。

import time
from pathos.helpers import mp

def foo(data, name=''):
    print(type(data), data.value, name)
    data.value += 1

if __name__ == "__main__":

    manager = mp.Manager()
    x = manager.Value('i', 0)
    y = mp.Value('i', 0)

    for i in range(3):
        mp.Process(target=foo, args=(x, 'x')).start()
        mp.Process(target=foo, args=(y, 'y')).start()

        print('Before waiting: ')
        print('x = {0}'.format(x.value))
        print('y = {0}'.format(y.value))

        time.sleep(3)
        print('After waiting: ')
        print('x = {0}'.format(x.value))
        print('y = {0}'.format(y.value))

【讨论】:

  • 好吧,我无法解决问题,但您的代码确实可以在 shell 中运行。我想使用多处理模块而不是第三方模块,但我想我现在必须使用 pathos。谢谢。
猜你喜欢
  • 2012-04-09
  • 2016-12-14
  • 1970-01-01
  • 2013-06-10
  • 1970-01-01
  • 1970-01-01
  • 2021-01-27
  • 2015-10-07
相关资源
最近更新 更多