【发布时间】: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 <module '__main__' (built-in)>,所以在脚本中运行时似乎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 3.8+ 的 Shared Memory Module 会有所帮助。
-
你的最后一个问题在这里stackoverflow.com/a/55084713/3625477得到了回答,基本上你不是在创建不同的线程,因为
Process.start()是创建这些线程的函数。
标签: python python-3.x multiprocessing