CPython 和线程模块 Lock()

CPython and threading module Lock()

由于 CPython 具有 GIL,因此不允许任何线程同时执行 python 代码,因此在给定进程中似乎存在线程安全。

那么 python 线程模块 Lock () 的目的是什么?即使没有线程可以同时执行,CPython 中仍然会出现哪些同步问题 Lock () 可以帮助解决?

GIL 只确保一次只有一个线程可以运行。仍然有可能一个线程在指令之间被中断,另一个线程有机会 运行。因此,如果两个线程访问共享资源,则需要使用锁来保护访问。

举个例子:

from threading import Thread

i = 0

def func():
    global i
    while i < 1000000:
        i += 1
        if i != i:
            print("i was modified")

for _ in range(10):
    Thread(target=func).start()

虽然看起来 if 条件永远不可能为真,但您很有可能会看到打印出的行。怎么可能?

如果您查看 func 的反汇编字节码(通过从 dis 模块调用 dis.dis(func)),您将得到以下内容:

  7           0 SETUP_LOOP              51 (to 54)
        >>    3 LOAD_GLOBAL              0 (i)
              6 LOAD_CONST               1 (1000000)
              9 COMPARE_OP               0 (<)
             12 POP_JUMP_IF_FALSE       53

  8          15 LOAD_GLOBAL              0 (i)
             18 LOAD_CONST               2 (1)
             21 INPLACE_ADD
             22 STORE_GLOBAL             0 (i)

  9          25 LOAD_GLOBAL              0 (i)
             28 LOAD_GLOBAL              0 (i)
             31 COMPARE_OP               3 (!=)
             34 POP_JUMP_IF_FALSE        3

 10          37 LOAD_GLOBAL              1 (print)
             40 LOAD_CONST               3 ('i was modified')
             43 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             46 POP_TOP
             47 JUMP_ABSOLUTE            3
             50 JUMP_ABSOLUTE            3
        >>   53 POP_BLOCK
        >>   54 LOAD_CONST               0 (None)
             57 RETURN_VALUE

相关的指令是25和28。如果线程在这两个指令之间被中断,另一个therad可以修改全局变量i并且存储的值会不同。