Python线程锁在简单示例中无效

10 投票
3 回答
11472 浏览
提问于 2025-04-18 16:16

我好像漏掉了什么,这个简单的例子中有两个线程试图在一个函数里修改一个全局变量,但结果却和预期的不一样:

from threading import Thread, Lock
some_var = 0

def some_func(id):
    lo = Lock()
    with lo:
        global some_var
        print("{} here!".format(id))
        for i in range(1000000):
            some_var += 1
        print("{} leaving!".format(id))


t1 = Thread(target=some_func, args=(1,))
t2 = Thread(target=some_func, args=(2,))
t1.start()
t2.start()
t1.join()
t2.join()
print(some_var)

输出结果:

1 here!
2 here!
2 leaving!
1 leaving!
1352010

你可以看到,两个线程同时进入了应该被锁住的部分,导致全局变量'some_var'的增加出现了混乱。

看起来这个锁根本没有起作用,不知道是什么原因。对于范围在10000以内的情况是有效的,但这可能只是因为在这么短的计算时间内,GIL(全局解释器锁)没有被释放。

到底发生了什么呢?

我使用的是Python3.3.2 64位版本。

3 个回答

3

你也可以在你的主函数(main())里定义一个锁,然后把这个锁传递给被调用的函数。

lock = threading.Lock()
t1 = Thread(target=some_func, args=(1,lock))
t2 = Thread(target=some_func, args=(2,lock))
t1.start()
t2.start()

这样的话就只有一个锁了。尽量避免使用全局变量是比较好的做法。

5

每次你的函数被调用时,都会创建一个新的锁,这样每个线程就会有不同的锁。为了让每个线程都能知道同一个锁是否被其他线程占用,锁的对象应该在全局范围内创建。试着把你的锁对象创建成一个全局锁吧!

14

Lock()这个函数会创建一个全新的锁,只有调用这个函数的线程才能使用这个锁。这就是它为什么不起作用的原因,因为每个线程都在锁住一个完全不同的锁。

锁的对象是少数几个可以安全声明为全局的东西之一,因为你肯定希望每个线程都能看到同一个Lock()。你应该尝试这样做:

from threading import Thread, Lock
some_var = 0
lo = Lock()

def some_func(id):
    global lo
    with lo:
        global some_var
        print("{} here!".format(id))
        for i in range(1000000):
            some_var += 1
        print("{} leaving!".format(id))

撰写回答