Python 多处理在Ubuntu中工作,不';Windows中的t

Python 多处理在Ubuntu中工作,不';Windows中的t,python,windows,multiprocessing,cherrypy,python-multiprocessing,Python,Windows,Multiprocessing,Cherrypy,Python Multiprocessing,我正在尝试在我的cherrypy应用程序上用作排队系统的模板 我能够将它从Python2转换为Python3(将从Queue import Empty更改为从Queue import Empty),并在Ubuntu中执行它。但当我在Windows中执行它时,会出现以下错误: F:\workspace\test>python test.py Traceback (most recent call last): File "test.py", line 112, in <module

我正在尝试在我的cherrypy应用程序上用作排队系统的模板

我能够将它从Python2转换为Python3(将
从Queue import Empty
更改为
从Queue import Empty
),并在Ubuntu中执行它。但当我在Windows中执行它时,会出现以下错误:

F:\workspace\test>python test.py
Traceback (most recent call last):
  File "test.py", line 112, in <module>
    broker.start()
  File "C:\Anaconda3\lib\multiprocessing\process.py", line 105, in start
    self._popen = self._Popen(self)
  File "C:\Anaconda3\lib\multiprocessing\context.py", line 212, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
  File "C:\Anaconda3\lib\multiprocessing\context.py", line 313, in _Popen
    return Popen(process_obj)
  File "C:\Anaconda3\lib\multiprocessing\popen_spawn_win32.py", line 66, in __init__
    reduction.dump(process_obj, to_child)
  File "C:\Anaconda3\lib\multiprocessing\reduction.py", line 59, in dump
    ForkingPickler(file, protocol).dump(obj)
TypeError: cannot serialize '_io.TextIOWrapper' object

F:\workspace\test>Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Anaconda3\lib\multiprocessing\spawn.py", line 100, in spawn_main
    new_handle = steal_handle(parent_pid, pipe_handle)
  File "C:\Anaconda3\lib\multiprocessing\reduction.py", line 81, in steal_handle
    _winapi.PROCESS_DUP_HANDLE, False, source_pid)
OSError: [WinError 87] The parameter is incorrect
Traceback (most recent call last):
  File "async2.py", line 121, in <module>
    broker.start()
  File "C:\python34\lib\multiprocessing\process.py", line 105, in start
    self._popen = self._Popen(self)
  File "C:\python34\lib\multiprocessing\context.py", line 212, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
  File "C:\python34\lib\multiprocessing\context.py", line 313, in _Popen
    return Popen(process_obj)
  File "C:\python34\lib\multiprocessing\popen_spawn_win32.py", line 66, in __init__
    reduction.dump(process_obj, to_child)
  File "C:\python34\lib\multiprocessing\reduction.py", line 60, in dump
    ForkingPickler(file, protocol).dump(obj)
_pickle.PicklingError: Can't pickle <class 'cherrypy.process.wspbus._StateEnum.State'>: attribute lookup State on cherrypy.process.wspbus failed

问题是
MyBus
对象的部分不可拾取,并且您正在将
MyBus
的实例保存到
Broker
实例中。由于Windows缺少
fork()
支持,因此在调用
broker.start()
时,必须在子进程中对
broker
的整个状态进行pickle并重新创建,该子进程是
多处理
生成的,以执行
broker.run
。它在Linux上工作,因为Linux支持
fork
;在这种情况下,它不需要对任何内容进行pickle处理——一旦分支,子进程就包含父进程的完整状态

解决这个问题有两种方法。第一种也是更困难的方法是使
代理
实例可拾取。为此,您需要使
MyBus
pickable。您现在遇到的错误是指
MyBus
上的
logger
属性,该属性不可拾取。那一个很容易修复;只需将
\uuu getstate\uuu
/
\uu setstate\uu
方法添加到
MyBus
,这些方法用于控制对象的pickle/unpickle方式。如果我们在pickle时删除记录器,并在取消pickle时重新创建记录器,我们将解决此问题:

class MyBus(wspbus.Bus):
    ... 
    def __getstate__(self):
        self_dict = self.__dict__
        del self_dict['logger']
        return self_dict

    def __setstate__(self, d):
        self.__dict__.update(d)
        self.open_logger()
这是可行的,但是我们遇到了另一个酸洗错误:

F:\workspace\test>python test.py
Traceback (most recent call last):
  File "test.py", line 112, in <module>
    broker.start()
  File "C:\Anaconda3\lib\multiprocessing\process.py", line 105, in start
    self._popen = self._Popen(self)
  File "C:\Anaconda3\lib\multiprocessing\context.py", line 212, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
  File "C:\Anaconda3\lib\multiprocessing\context.py", line 313, in _Popen
    return Popen(process_obj)
  File "C:\Anaconda3\lib\multiprocessing\popen_spawn_win32.py", line 66, in __init__
    reduction.dump(process_obj, to_child)
  File "C:\Anaconda3\lib\multiprocessing\reduction.py", line 59, in dump
    ForkingPickler(file, protocol).dump(obj)
TypeError: cannot serialize '_io.TextIOWrapper' object

F:\workspace\test>Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Anaconda3\lib\multiprocessing\spawn.py", line 100, in spawn_main
    new_handle = steal_handle(parent_pid, pipe_handle)
  File "C:\Anaconda3\lib\multiprocessing\reduction.py", line 81, in steal_handle
    _winapi.PROCESS_DUP_HANDLE, False, source_pid)
OSError: [WinError 87] The parameter is incorrect
Traceback (most recent call last):
  File "async2.py", line 121, in <module>
    broker.start()
  File "C:\python34\lib\multiprocessing\process.py", line 105, in start
    self._popen = self._Popen(self)
  File "C:\python34\lib\multiprocessing\context.py", line 212, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
  File "C:\python34\lib\multiprocessing\context.py", line 313, in _Popen
    return Popen(process_obj)
  File "C:\python34\lib\multiprocessing\popen_spawn_win32.py", line 66, in __init__
    reduction.dump(process_obj, to_child)
  File "C:\python34\lib\multiprocessing\reduction.py", line 60, in dump
    ForkingPickler(file, protocol).dump(obj)
_pickle.PicklingError: Can't pickle <class 'cherrypy.process.wspbus._StateEnum.State'>: attribute lookup State on cherrypy.process.wspbus failed
状态
对象(惊喜)用于跟踪
总线
实例的状态。因为我们在启动总线之前进行酸洗,所以我们可以在酸洗时从对象中删除
state
属性,并将其设置为States.STOPPED(当我们取消勾选时)

class MyBus(wspbus.Bus):
    def __init__(self, name=""):
        wspbus.Bus.__init__(self)
        self.open_logger(name)
        self.subscribe("log", self._log)

    def __getstate__(self):
        self_dict = self.__dict__
        del self_dict['logger']
        del self_dict['state']
        return self_dict

    def __setstate__(self, d):
        self.__dict__.update(d)
        self.open_logger()
        self.state = wspbus.states.STOPPED  # Initialize to STOPPED
通过这些更改,代码可以按预期工作!唯一的限制是,只有在总线尚未启动的情况下,才可以安全地pickle
MyBus
,这对于您的用例来说是很好的

同样,这是一条艰难的道路。简单的方法是完全不需要pickle
MyBus
实例。您只需在子进程中创建
MyBus
实例,而不是父进程:

class Broker(Process):
    def __init__(self, queue):
        Process.__init__(self)
        self.queue = queue

...
    def run(self):
        self.bus = MyBus(Broker.__name__)  # Create the instance here, in the child
        self.bus.subscribe("main", self.check)
        self.bus.start()
        self.bus.block(interval=0.01)

只要您不需要访问父级中的
broker.bus
,这是一个更简单的选项。

哇,我印象深刻!!很好的解释。嗯。。。Windows中缺少
fork()
是否也意味着为了在一个包含数百个类的大型web应用程序上运行一个小类的小方法,每次启动一个新进程时都会从头开始解释整个shebang?这是我应该担心的吗?@stenci脚本的
\uuuu main\uuuuu
模块将重新导入子级,并且调用方法的类的整个状态将被pickle并发送给子级。如果您已经在
If\uuuu name\uuuu==“\uuuuu main\uuuu”:
guard(您应该这样做)中创建了其他类,并且您的小类没有对其他类的任何引用,那么它们将不会被pickle并发送到子类中或重新导入到子类中。要记住的最重要的一点是,如果uuu name uuuuu==“uuuu main uuuuuu”:,请保护您不想在子级中重新运行的内容。谢谢,现在这样做很有意义(暂时没有)