在类方法Python中调用多处理
最初,我有一个类来存储一些已处理的值,并将这些值与其其他方法一起重用 问题是,当我试图将类方法划分为多个进程以提高速度时,python生成了进程,但它似乎不起作用(正如我在任务管理器中看到的,只有一个进程在运行),并且结果从未交付 我做了几次搜索,发现pathos.multiprocessing可以代替它,但我想知道标准库是否可以解决这个问题在类方法Python中调用多处理,python,methods,multiprocessing,Python,Methods,Multiprocessing,最初,我有一个类来存储一些已处理的值,并将这些值与其其他方法一起重用 问题是,当我试图将类方法划分为多个进程以提高速度时,python生成了进程,但它似乎不起作用(正如我在任务管理器中看到的,只有一个进程在运行),并且结果从未交付 我做了几次搜索,发现pathos.multiprocessing可以代替它,但我想知道标准库是否可以解决这个问题 from multiprocessing import Pool class A(): def __init__(self, vl):
from multiprocessing import Pool
class A():
def __init__(self, vl):
self.vl = vl
def cal(self, nb):
return nb * self.vl
def run(self, dt):
t = Pool(processes=4)
rs = t.map(self.cal, dt)
t.close()
return t
a = A(2)
a.run(list(range(10)))
您的代码失败,因为它无法
pickle
实例方法(self.cal
),这是Python在生成多个进程时试图做的事情,方法是将它们映射到multiprocessing.Pool
(当然,有一种方法可以做到这一点,但它太复杂了,而且没有太大用处)-由于没有共享内存访问,它必须“打包”数据并将其发送到衍生进程进行解包。如果试图对实例进行pickle处理,也会发生同样的情况
多处理
软件包中唯一可用的共享内存访问是一个鲜为人知的多处理.pool.ThreadPool
,因此如果您真的想这样做:
from multiprocessing.pool import ThreadPool
class A():
def __init__(self, vl):
self.vl = vl
def cal(self, nb):
return nb * self.vl
def run(self, dt):
t = ThreadPool(processes=4)
rs = t.map(self.cal, dt)
t.close()
return rs
a = A(2)
print(a.run(list(range(10))))
# prints: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
但这不会给你并行化,因为它本质上映射到你的常规线程,而这些线程确实可以访问共享内存。您应该传递类/静态方法(如果需要调用它们)以及希望它们使用的数据(在您的例子中是self.vl
)。如果您需要跨进程共享数据,您必须使用一些共享内存抽象,比如multiprocessing.Value
,当然要在整个过程中应用互斥
更新
我说过你可以做到(有些模块或多或少都在做,比如检查pathos.multiprocessing
),但我认为这不值得麻烦——当你不得不欺骗你的系统去做你想做的事情时,很可能是你使用了一个错误的系统,或者你应该重新考虑你的设计。但出于信息的考虑,这里有一种方法可以在多处理设置中实现您想要的功能:
import sys
from multiprocessing import Pool
def parallel_call(params): # a helper for calling 'remote' instances
cls = getattr(sys.modules[__name__], params[0]) # get our class type
instance = cls.__new__(cls) # create a new instance without invoking __init__
instance.__dict__ = params[1] # apply the passed state to the new instance
method = getattr(instance, params[2]) # get the requested method
args = params[3] if isinstance(params[3], (list, tuple)) else [params[3]]
return method(*args) # expand arguments, call our method and return the result
class A(object):
def __init__(self, vl):
self.vl = vl
def cal(self, nb):
return nb * self.vl
def run(self, dt):
t = Pool(processes=4)
rs = t.map(parallel_call, self.prepare_call("cal", dt))
t.close()
return rs
def prepare_call(self, name, args): # creates a 'remote call' package for each argument
for arg in args:
yield [self.__class__.__name__, self.__dict__, name, arg]
if __name__ == "__main__": # important protection for cross-platform use
a = A(2)
print(a.run(list(range(10))))
# prints: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
我认为它的工作原理是不言自明的,但简而言之,它将类的名称、当前状态(sans signals,tho)、要调用的所需方法和要调用的参数传递给并行调用
函数,该函数为池中的每个进程调用。Python会自动对所有这些数据进行pickle和unpickle,因此所有的parallel_调用
需要做的就是重建原始对象,在其中找到所需的方法,并使用提供的参数调用它
这样,我们只传递数据,而不尝试传递活动对象,这样Python就不会抱怨了(在本例中,尝试向类参数中添加对实例方法的引用,看看会发生什么),一切都正常
如果你想重拾“魔力”,你可以让它看起来和你的代码一模一样(创建你自己的池
处理程序,从函数中提取名称并将名称发送到实际进程,等等),但这对于你的示例来说应该是一个足够的函数
但是,在您满怀希望之前,请记住,只有在共享“静态”实例(在多处理上下文中开始调用时不会更改其初始状态的实例)时,此操作才会起作用。如果A.cal
方法要更改vl
属性的内部状态,则只会影响其更改的实例(除非在调用之间调用池的主实例中更改)。如果您也想共享状态,可以升级parallel\u call
以拾取实例。在调用后,将其与方法调用结果一起返回,然后在调用端,您必须使用返回的数据更新本地\u dict\u
,以更改原始状态。这还不够——实际上,您必须创建一个共享dict并处理所有互斥工作人员,以便所有进程都能同时访问它(您可以使用multiprocessing.Manager
)
所以,就像我说的,麻烦比它的价值还多
问题:似乎不起作用(正如我在任务管理器中看到的,只有一个进程正在运行)
结果永远无法实现
您只看到一个进程作为池
计算已使用进程的数量,如下所示:
您给出了范围(10)
=任务索引0..9,因此池计算(10/4)*4=8+1=9
启动第一个过程后
没有剩余任务。
使用范围(32)
,您将看到4进程正在运行
您正在返回returnt
,而不是返回rs=pool.map(…
)的结果
例如,这将起作用
def cal(self, nb):
import os
print('pid:{} cal({})'.format(os.getpid(), nb))
return nb * self.vl
def run(self,df):
with mp.Pool(processes=4) as pool:
return pool.map(self.cal, df)
if __name__ == '__main__':
a = A(2)
result = a.run(list(range(32)))
print(result)
用Python测试:3.4.2使用如果uuu name uuu=='uu main uuu'
guard。这似乎根本不需要多处理。您使用的是更大的数据还是其他什么?还是另一种方法?生成一个进程来执行一次乘法的开销是不值得的。不久前出现了一个类似的问题使用joblib
;使用numpy会更快,因为它需要为一个新进程复制和切换上下文。因此,在我的情况下,没有解决方案,我们可以使用类方法通过使用类中的共享内存来发送多个进程的垃圾邮件,对吗?正确的方法只是将生成方法带到类之外吗re是一个解决方案,我刚刚更新了我的答案。它很笨重,也不是很好,但它很管用……再说一遍,我建议您重新考虑您的设计,这样您就不必处理共享实例的状态。没有理由让自己比