代码之家  ›  专栏  ›  技术社区  ›  Tom

是否在python threasafe中修改类变量?

  •  15
  • Tom  · 技术社区  · 16 年前

    我在读书 this question (你不必阅读,因为我会复制那里的内容…我只是想给你看看我的灵感……

    因此,如果我有一个计算创建了多少实例的类:

    class Foo(object):
      instance_count = 0
      def __init__(self):
        Foo.instance_count += 1
    

    我的问题是,如果我在多个线程中创建foo对象,实例计数是否正确?从多个线程修改类变量是否安全?

    3 回复  |  直到 16 年前
        1
  •  23
  •   Ants Aasma    16 年前

    即使在塞顿也不是螺纹安全。自己试试看:

    import threading
    
    class Foo(object):
        instance_count = 0
    
    def inc_by(n):
        for i in xrange(n):
            Foo.instance_count += 1
    
    threads = [threading.Thread(target=inc_by, args=(100000,)) for thread_nr in xrange(100)]
    for thread in threads: thread.start()
    for thread in threads: thread.join()
    
    print(Foo.instance_count) # Expected 10M for threadsafe ops, I get around 5M
    

    原因是,虽然inplace_add在gil下是原子的,但属性仍然被加载和存储(请参见 dis.dis(foo.\uuu init\uuuuu) )使用锁序列化对类变量的访问:

    Foo.lock = threading.Lock()
    
    def interlocked_inc(n):
        for i in xrange(n):
            with Foo.lock:
                Foo.instance_count += 1
    
    threads = [threading.Thread(target=interlocked_inc, args=(100000,)) for thread_nr in xrange(100)]
    for thread in threads: thread.start()
    for thread in threads: thread.join()
    
    print(Foo.instance_count)
    
        2
  •  8
  •   luc    16 年前

    不,它不是线程安全的。几天前我也遇到过类似的问题,多亏了一个装饰师,我选择了实现锁。其好处是它使代码可读:

    def threadsafe_function(fn):
        """decorator making sure that the decorated function is thread safe"""
        lock = threading.Lock()
        def new(*args, **kwargs):
            lock.acquire()
            try:
                r = fn(*args, **kwargs)
            except Exception as e:
                raise e
            finally:
                lock.release()
            return r
        return new
    
    class X:
        var = 0
    
        @threadsafe_function     
        def inc_var(self):
            X.var += 1    
            return X.var
    
        3
  •  -4
  •   user84491    16 年前

    我会说它是线程安全的,至少在cpython实现上是如此。gil将使您的所有“线程”按顺序运行,这样它们就不会干扰您的引用计数。