Python 类方法是线程安全的吗?

Python 类方法是线程安全的吗?,python,multithreading,python-3.x,class-method,Python,Multithreading,Python 3.x,Class Method,我正在处理一个在多线程环境中运行的类,看起来像这样(去除了多余的噪声): 我担心的是,两个线程可能同时调用apply或do\u thing,或者子类可能会尝试在其中一个函数中对cls执行一些愚蠢的操作。我可以使用staticmethod而不是classmethod,但是调用do\u thing会变得复杂得多,特别是如果子类重新实现其中一个而不是另一个。所以我的问题是:上面的类是线程安全的,还是使用这样的类方法存在潜在问题?在这方面,类方法和常规函数(以及实例方法)没有区别。两者都不是自动线程安全

我正在处理一个在多线程环境中运行的类,看起来像这样(去除了多余的噪声):


我担心的是,两个线程可能同时调用
apply
do\u thing
,或者子类可能会尝试在其中一个函数中对
cls
执行一些愚蠢的操作。我可以使用
staticmethod
而不是
classmethod
,但是调用
do\u thing
会变得复杂得多,特别是如果子类重新实现其中一个而不是另一个。所以我的问题是:上面的类是线程安全的,还是使用这样的类方法存在潜在问题?

在这方面,类方法和常规函数(以及实例方法)没有区别。两者都不是自动线程安全的


如果一个或多个classmethods/methods/Function可以从不同线程同时操作数据结构,则需要添加同步保护,通常使用
threading.Lock
s.

方法是否线程安全取决于该方法的功能

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

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

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

无论如何,您在这里所做的是使用进程而不是线程。在这种情况下,对象将被pickle并通过管道发送到另一个进程,在那里它们将被修改并发送回。与线程相反,进程不共享内存。因此,我认为在类方法中使用锁不会产生效果


另外两个答案在技术上都是正确的,因为
do_thing()
的安全性取决于函数内部发生的情况

但更准确的答案是,电话本身是安全的。换句话说,如果
apply()
do\u thing()
是a,那么您的代码是安全的。任何不安全性都是由于它们不是纯函数(例如,在执行过程中依赖或影响共享变量)

正如shx2所提到的,classmethods仅在可视的情况下“在”一个类中,用于分组。它们对类的任何实例都没有固有的附件。因此,该代码在功能上大致相当:

关于并发性的进一步说明给出了其他答案:

  • threading.Lock
    很容易理解,但应该是您最后的选择。在幼稚的实现中,它通常比完全线性处理慢。如果您可以使用诸如
    线程化.Event
    队列.queue
    多处理.Pipe
    之类的方法来传输信息,那么您的代码通常会更快
  • asyncio
    是python3的新热点。这是一个有点难以得到正确的,但通常是最快的方法
  • 如果您想了解python中的现代并发技术,请查看CoreDeveloper。这一切都很好,但是
    lock
    的缺点从t=开始突出显示

  • 谢谢你的回答。那么简单地调用
    cls.do\u thing
    没有锁会很危险吗?直觉上我不这么认为,但我在多线程方面没有太多经验。
    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)
    
    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)