请教下 Python 多进程类方法 pickle 的问题

2019-11-19 16:23:33 +08:00
 xiaolinjia

我用 multiprocessing.Pool 来对类方法进行多进程。 然后以下报错: cPickle.PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup builtin.instancemethod failed

之后查了一下,说 multiprocessing 默认只支持不在类里面的函数或者类里面的 @staticmethod,于是用

import copy_reg
import types
def _pickle_method(m):
    if m.im_self is None:
        return getattr, (m.im_class, m.im_func.func_name)
    else:
        return getattr, (m.im_self, m.im_func.func_name)


copy_reg.pickle(types.MethodType, _pickle_method)

这段代码,解决了上面的错误,但是又以下报错: TypeError: can't pickle thread.lock objects

然后又查了貌似是类中有些变量不能序列化导致的,然后想请教下这种情况怎么解决啊。 我看了些资料说是可以重写__getstate__和__setstate__魔法方法,但也不知道其参数是怎么传的。也有说将类设成全局变量,然后 pool.apply 的时候就不需要把类传进去,但是这样发现,类会初始化多次,请问要怎么解决呢?

4708 次点击
所在节点    Python
17 条回复
wuwukai007
2019-11-20 09:21:21 +08:00
pip install dill
dill.dumps(obj)
xiaolinjia
2019-11-20 09:30:34 +08:00
@wuwukai007 这个 obj 指的是?
wuwukai007
2019-11-20 09:34:03 +08:00
你要序列化的对象啊,可以是函数,类,内嵌函数,用的时候在 dill.loads(obj)
wuwukai007
2019-11-20 09:34:53 +08:00
a = dill.dumps(obj)
把 a 当作参数放到 进程的 args 里面
xiaolinjia
2019-11-20 09:36:51 +08:00
@wuwukai007 我进程的 args 里面的参数是可以 pickle 的,可能是进程里的类方法里有些类成员不能 pickle。
wuwukai007
2019-11-20 09:46:54 +08:00
你开多进程,把这个类序列化,传进去当参数,类里面的方法会报错?
xiaolinjia
2019-11-20 09:53:19 +08:00
@wuwukai007 我补充了一下问题,我调用的时候是在 main 方法中:
client = Client()
from multiprocessing import Pool
pool = Pool(processes=2)
for url in ['ws://192.168.0.18:9988/websocket/37', 'ws://192.168.0.18:9988/websocket/44']:
pool.apply_async(func=client.received_message, args=(url,))
pool.close()
pool.join()
xiaolinjia
2019-11-20 09:54:24 +08:00
@xiaolinjia 那个空格被吃了,
client = Client()
from multiprocessing import Pool
pool = Pool(processes=2)
for url in ['ws://192.168.0.18:9988/websocket/37', 'ws://192.168.0.18:9988/websocket/44']:
----pool.apply_async(func=client.received_message, args=(url,))
pool.close()
pool.join()
hustlibraco
2019-11-20 10:00:21 +08:00
一般不建议这么搞,进程之间共享的数据最好只是整型、字符串,每个进程单独创建一个 client 比较好。强行修改序列化对象一个是不安全,一个是复杂不可重用。
xiaolinjia
2019-11-20 10:14:13 +08:00
@hustlibraco 其实我也有想过这个,不过就是单独创建一个实例的话,会多次初始化,但是我又想只初始化一次。
aaronhua
2019-11-20 10:18:25 +08:00
看着自己堆的一坨坨 shi 山,不禁感慨,有时候代码简单点,效率低点也没有关系。
wuwukai007
2019-11-20 10:33:44 +08:00
socket 对象好像不能被序列化把
xiaolinjia
2019-11-20 10:56:30 +08:00
@wuwukai007 socket 这里,我刚才单独测试的时候应该是没问题,因为他是每个进程内,才创建的。应该不会参与序列化这块。。
wuwukai007
2019-11-20 11:33:31 +08:00
copy_reg.pickle(types.MethodType, _pickle_method)
你这一步报错
把这一步换成
dill.dumps(_pickle_method)
试一下,
xiaolinjia
2019-11-21 14:47:17 +08:00
@wuwukai007 这个连第一步都失败了,现在总结得出,应该是有个数据库的持续化链接的问题,就是 self.db 。其他的 websocket,rabbitmq,因为都是在进程内创建的都没问题,应该就是剩下这个 oracle 的数据库链接不能 pickle 了
wuwukai007
2019-11-21 15:01:29 +08:00
oracle 数据库连接不能序列化, 用连接池 pip install DBUtils
hustlibraco
2019-11-22 14:00:55 +08:00
@xiaolinjia 不知道你有没有测试过,序列化对象的性能开销和对象重新创建的性能开销,谁更大

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/621076

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX