实例变量的线程问题

2 投票
1 回答
1605 浏览
提问于 2025-04-16 23:31

看起来我对Python的一些基本概念有点误解。把一个实例变量传给一个函数,应该只是传递这个对象的引用,也就是说,如果这个对象是不可变的,像这样做 self.var = "something"; foo(self.var) 不应该改变self.var的值,只要一切正常。

但是现在考虑这个情况:

import threading

class Test():
    def __init__(self):
        self.lock = threading.Lock()
        self.arg = "arg0"

    def foo(self, i):
        with self.lock:
            threading.Thread(target=lambda: bar(self.arg)).start()
            self.arg = "arg" + str(i)

def bar(arg):
    import time
    time.sleep(1)
    print("Bar: " + arg)

if __name__ == '__main__':
    t = Test()
    for i in range(1, 6):
        t.foo(i)

我创建了一个线程对象,并且给它传递了当前字符串的引用,然后我更新了这个字符串——而这个线程不应该看到这个更新。由于有锁的存在,下一条线程也应该在更新之后才开始运行——所以虽然我不能确定arg0到arg5的打印顺序,但我认为每个arg应该只打印一次。但是我得到了以下输出(Win7 x64,python 3.1 x64)

Bar: arg0
Bar: arg2
Bar: arg2
Bar: arg5
Bar: arg3

编辑:好吧,写完这些后,我突然想到,可能lambda表达式并不是在创建线程时执行的,而是在之后执行的,这就解释了这个行为。所以简单的解决办法就是创建一个局部变量并使用它。嗯——这真是得到了快速的帮助,感谢SO ;)

1 个回答

2

我发现我还没有回答这个问题,所以现在来解释一下:

这个 lambda 表达式会创建一个对 self 的闭包,但并不是对 self.arg 本身的闭包。这意味着当我们稍后执行 bar 时,我们访问的是最新的值,而不是 lambda 创建时的那个值。

撰写回答