在Python中,如果想使用多进程来处理类实例,会面临一个问题:类方法无法被腌制(pickle),因此无法通过管道发送给子进程。这导致无法将类实例中的方法发送到子进程中执行。
2. 解决方案
为了解决这个问题,可以采用以下方法:
-
将类方法转换为可腌制对象。这可以通过使用functools.partial函数来实现。functools.partial函数可以将类方法转换为一个可调用的对象,这个对象可以被腌制。
-
将类实例 pickle 化,并在子进程中反序列化。这可以通过使用pickle模块来实现。pickle模块可以将对象序列化为字节流,然后在子进程中反序列化为对象。
-
使用共享内存。这可以通过使用multiprocessing.Manager类来实现。multiprocessing.Manager类可以创建共享内存,然后子进程可以访问共享内存中的数据。
下面提供一个使用functools.partial函数来解决这个问题的示例代码:
import multiprocessing
import functools
class ProcessWorker(multiprocessing.Process):
def __init__(self, task_q, result_q):
multiprocessing.Process.__init__(self)
self.task_q = task_q
self.result_q = result_q
def run(self):
while True:
next_task = self.task_q.get()
if next_task is None:
# Poison pill means shutdown
print('%s: Exiting' % (self.name))
self.task_q.task_done()
break
# 使用functools.partial将类方法转换为可腌制对象
task = functools.partial(self.run_task, *next_task[1], **next_task[2])
answer = task()
self.task_q.task_done()
self.result_q.put(answer)
def run_task(self, *args, **kwargs):
print('%s: %s' % (self.name, next_task))
return next_task(*args, **kwargs)
class Worker(object):
def __init__(self, config, index=None):
self.config = config
self.index = index
# 启动 ProcessWorker
if self.index is not None:
self.task_q = multiprocessing.JoinableQueue()
self.result_q = multiprocessing.Queue()
self.process_worker = ProcessWorker(self.task_q, self.result_q)
self.process_worker.start()
def enqueue_process(target): # No self, since it is a decorator
# 使用functools.partial将类方法转换为可腌制对象
task = functools.partial(target, self)
def wrapper(*args, **kwargs):
self.task_q.put([task, args, kwargs])
return wrapper
def fetch_results(self):
# 等待子进程完成任务
self.task_q.join()
# 获取子进程的结果
return self.result_q.get()
@enqueue_process
def run_long_command(self, command):
print("I am running number % as process " % number, self.name)
# 在子进程中执行命令
p = Popen(command)
p.wait()
def close(self):
self.task_q.put(None)
self.task_q.join()
if __name__ == '__main__':
config = ["some value", "something else"]
index = 7
workers = []
for i in range(5):
worker = Worker(config, index)
worker.run_long_command("ls /")
workers.append(worker)
for worker in workers:
worker.fetch_results()
# Do more work... (this would actually be done in a distributor in another class)
for worker in workers:
worker.close()
在这个示例中,ProcessWorker类是一个多进程类,负责执行任务。Worker类是一个封装了ProcessWorker类的类,用于管理任务和结果。
在使用这个示例代码时,需要注意以下几点:
- 需要先创建Worker类的实例,然后才能使用enqueue_process装饰器来装饰类方法。
- 在子进程中执行任务时,需要使用functools.partial将类方法转换为可腌制对象。
- 在主进程中等待子进程完成任务时,需要使用task_q.join()方法。
- 在主进程中获取子进程的结果时,需要使用result_q.get()方法。