我正在开发一个在多线程环境中运行的类,看起来像这样(去除了过多的噪声):
class B:
@classmethod
def apply(cls, item):
cls.do_thing(item)
@classmethod
def do_thing(cls, item)
'do something to item'
def run(self):
pool = multiprocessing.Pool()
for list_of_items in self.data_groups:
pool.map(list_of_items, self.apply)
我担心的是两个线程可能同时调用apply
或do_thing
,或者一个子类可能会尝试在其中一个函数中使用cls
做一些愚蠢的事情。我可以使用staticmethod
代替classmethod
,但调用do_thing
会变得复杂得多,特别是如果子类重新实现其中一个而不是另一个。所以我的问题是:上面的类是线程安全的,还是使用像这样的类方法有潜在的问题?
答案 0 :(得分:3)
在这方面,类方法和常规函数(以及实例方法)之间没有区别。两者都不是自动线程安全的。
如果一个或多个类方法/方法/函数可以同时处理来自不同线程的数据结构,则需要添加同步保护,通常使用threading.Lock
s。
答案 1 :(得分:3)
方法是否是线程安全取决于方法的作用。
仅使用局部变量是线程安全的。但是,当您从不同的线程更改相同的非局部变量时,它变得不安全。
‘do something to item’
似乎只修改给定的对象,该对象独立于列表中的任何其他对象,因此它应该是线程安全的。
如果同一个对象在列表中多次,您可能需要考虑使对象线程安全。这可以通过在修改对象的每个方法中使用with self.object_scope_lock:
来完成。
无论如何,你在这里做的是使用进程而不是线程。在这种情况下,对象被pickle并通过管道发送到另一个进程,在那里它们被修改并发回。与线程进程相比,不共享内存。所以我认为在类方法中使用锁会产生影响。
http://docs.python.org/3/library/threading.html?highlight=threading#module-threading
答案 2 :(得分:1)
其他两个答案在技术上都是正确的,因为do_thing()
的安全性取决于函数内部发生的事情。
但更准确的答案是呼叫本身是安全的。换句话说,如果apply()
和do_thing()
是pure functions,那么您的代码是安全的。任何不安全因素都是由于它们不是纯函数(例如在执行期间依赖或影响共享变量)
正如shx2所提到的,类方法只是"在"视觉上的一个类,用于分组。它们对类的任何实例都没有固有的附件。因此,此代码在功能上大致相同:
def apply(item):
do_thing(item)
def do_thing(item)
'do something to item'
class B:
def run(self):
pool = multiprocessing.Pool()
for list_of_items in self.data_groups:
pool.map(list_of_items, apply)
关于并发性的另一个注释,给出了其他答案:
threading.Lock
很容易理解,但应该是您的最后手段。在天真的实现中,它通常比完全线性处理慢。如果您可以使用threading.Event
,queue.Queue
或multiprocessing.Pipe
等内容来转移信息,那么您的代码通常会更快。asyncio
是python3中的新热点。要做到这一点有点困难,但通常是最快的方法。 lock
的缺点从t = 57:59开始突出显示。