我正在创建一个类的对象(带multiprocessing
)并将其添加到Manager.dict()
,以便我可以从对象内的字典中删除该项(该项指向)当它的工作完成时......
我尝试了以下代码:
from multiprocessing import Manager, Process
class My_class(Process):
def __init__(self):
super(My_class, self).__init__()
print "Object", self, "created."
def run(self):
print "Object", self, "process started."
manager=Manager()
object_dict=manager.dict()
for x in range(2):
object_dict[x]=My_class()
object_dict[x].start()
但我收到了一个错误:
TypeError: Pickling an AuthenticationString object is disallowed
for security reasons
为了好奇,我删除了多处理部分,并尝试了:
from multiprocessing import Manager
class My_class():
def __init__(self):
print "Object", self, "created."
manager=Manager()
object_dict=manager.dict()
for x in range(2):
object_dict[x]=My_class()
并且它没有给我任何错误并显示两个对象的地址。
该错误是什么以及如何使其消失?
答案 0 :(得分:5)
这是复制您所看到的效果的较短方法:
from multiprocessing import Process
import pickle
p = Process()
pickle.dumps(p._config['authkey'])
TypeError:不允许腌制AuthenticationString对象 出于安全原因
这里实际发生的事情如下:process._config['authkey']
是Process
对象在创建时被分配的secret key。尽管此键仅是字节序列,但Python使用bytes
的特殊子类来表示它:AuthenticationString
。该子类仅在一个方面与通常的bytes
不同-它拒绝被腌制。
此选择背后的原理如下:authkey用于验证父级和子级进程之间(例如,工作进程与主进程之间)的进程间通信消息,并将其暴露在初始进程族之外的任何地方都可能构成安全风险(因为从原则上讲,您可以为工作人员模拟“父进程”,并迫使其执行任意代码)。由于酸洗是Python中最常见的数据传输形式,因此禁止酸洗是一种意外暴露authkey的简单方法。
由于您不能腌制AuthenticationString
,所以也不能腌制Process
类或其任何子类的实例(因为它们都在字段中包含身份验证密钥)。
现在让我们看看它们与您的代码之间的关系。您创建一个Manager
对象,并尝试设置其dict
的值。 Manager
实际上是在一个单独的进程中运行,每当您向manager.dict()
分配任何数据时,Python都需要将此数据传输到Manager's
自己的进程中。为了进行这种传输,需要对数据进行腌制。但是,从前几段中我们知道,您不能腌制Process
对象,因此根本无法将它们保留在共享的dict
中。
简而言之,您可以自由使用manager.dict()
共享任何对象,除了那些不能腌制的对象,例如Process
对象。
答案 1 :(得分:0)
注意:下面的解决方案是在 Python3 aka print() 中。 Python3 中也存在同样的问题。
好吧,在您的具体示例中,我们可以通过将 AuthenticationString
dict 中的 _config
酸洗作为字节缓冲区作为 Process
对象的一部分,然后优雅地恢复来解决该问题它在解酸时就好像什么也没发生一样。在 My_class
中定义为酸洗操作调用的 get 和 set 状态方法,如下所示:
from multiprocessing import Manager, Process
from multiprocessing.process import AuthenticationString
class My_class(Process):
def __init__(self):
super(My_class, self).__init__()
print("Object", self, "created.")
def run(self):
print("Object", self, "process started.")
def __getstate__(self):
"""called when pickling - this hack allows subprocesses to
be spawned without the AuthenticationString raising an error"""
state = self.__dict__.copy()
conf = state['_config']
if 'authkey' in conf:
#del conf['authkey']
conf['authkey'] = bytes(conf['authkey'])
return state
def __setstate__(self, state):
"""for unpickling"""
state['_config']['authkey'] = AuthenticationString(state['_config']['authkey'])
self.__dict__.update(state)
if __name__ == '__main__': # had to add this
manager=Manager()
object_dict=manager.dict()
for x in range(2):
object_dict[x]=My_class()
object_dict[x].start()
我通过运行代码得到以下输出:
Object <My_class(My_class-2, initial)> created.
Object <My_class(My_class-3, initial)> created.
Object <My_class(My_class-2, started)> process started.
Object <My_class(My_class-3, started)> process started.
这似乎是预期的结果,如果您输入 time.sleep()
调用以保持它们的存活时间更长一些,您可以看到两个子进程正在运行。
或者,如果您只是删除该 _config
authkey
,然后您甚至不需要定义自定义 __setstate__
方法,它似乎也不会造成任何问题。
另外,请注意,我必须添加 __main__
- 如果没有它,python 会抱怨在启动子进程之前没有完成引导。
最后,我只需要对这整个“安全”事情耸耸肩。它会在各种地方弹出(需要相同类型的变通方法)并且不提供任何真正的安全性。