classmethods是线程安全的吗?

3

我正在开发一个在多线程环境下操作的类,它看起来像这样(已删除冗余噪音):

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,或者一个子类可能在其中一个函数中尝试做一些愚蠢的事情。我可以使用 staticmethod 代替 classmethod,但调用 do_thing 就会变得更加复杂,特别是如果子类重新实现其中一个而不是另一个。因此,我的问题是:上述类是否是线程安全的,或者使用 classmethod 存在潜在问题?

3个回答

3

就这个问题而言,类方法和普通函数(以及实例方法)之间没有区别。二者都不能自动保证线程安全。

如果一个或多个类方法/方法/函数可以在不同的线程中同时操作数据结构,则需要添加同步保护,通常使用 threading.Lock 实现。


谢谢你的回答。那么,如果没有锁,仅仅调用cls.do_thing会有危险吗?直觉上我觉得不会,但是我对多线程没有太多经验。 - aquavitae

3

一个方法是否线程安全取决于该方法的操作。

仅使用本地变量是线程安全的。但是当您从不同的线程更改相同的非本地变量时,它就会变得不安全。

'对项目执行某些操作' 似乎只修改给定对象,该对象独立于列表中的任何其他对象,因此应该是线程安全的。

如果同一对象在列表中出现多次,则可能需要考虑使该对象线程安全。这可以通过在每个修改对象的方法中使用 with self.object_scope_lock: 来完成。

无论如何,在这里你正在使用进程而不是线程。在这种情况下,对象被 pickled 并通过管道发送到另一个进程,在那里进行修改并发送回来。与线程不同,进程不共享内存。因此,我认为在类方法中使用锁不会产生影响。

http://docs.python.org/3/library/threading.html?highlight=threading#module-threading


3
其他两个答案从技术上来说都是正确的,因为do_thing()的安全性取决于函数内部发生了什么。但更精确的答案是,调用本身是安全的。换句话说,如果apply()do_thing()纯函数,那么你的代码就是安全的。任何不安全性都是由于它们不是纯函数(例如在执行过程中依赖或影响共享变量)引起的。
正如shx2所提到的,classmethods只是在视觉上“属于”类别,用于分组。它们与类的任何实例没有固有的关联。因此,这段代码在功能上大致相当:
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)

关于并发性的进一步说明,考虑到其他答案:

  1. threading.Lock很容易理解,但应该是最后的选择。在天真的实现中,它通常比完全线性处理慢。如果您可以使用像threading.Eventqueue.Queuemultiprocessing.Pipe这样的东西来传输信息,那么您的代码通常会更快。
  2. asyncio是Python3中最新的热门技术。它有点难以掌握,但通常是最快的方法。
  3. 如果您想了解Python中现代并发技术的详细步骤,请查看核心开发者Raymond Hettinger的并发演讲。整个演讲都非常好,但从t=57:59开始,强调了lock的缺点。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接