为什么Python提供锁定机制,如果它受GIL的约束?

dan*_*vic 7 python multithreading gil locks python-multiprocessing

我知道Python线程一次只能执行一个字节码,那么为什么线程库会提供锁?我假设如果一次只执行一个线程,则不会发生竞争条件.

该库提供锁,条件和信号量.这是同步执行的唯一目的吗?

更新:

我做了一个小实验:

from threading import Thread
from multiprocessing import Process

num = 0

def f():
    global num
    num += 1

def thread(func):
    # return Process(target=func)
    return Thread(target=func)


if __name__ == '__main__':
    t_list = []
    for i in xrange(1, 100000):
        t = thread(f)
        t.start()
        t_list.append(t)

    for t in t_list:
        t.join()

    print num
Run Code Online (Sandbox Code Playgroud)

基本上我应该启动100k线程并递增1.返回的结果是99993.

a)如果有GIL同步和避免竞争条件,结果如何不是99999?b)甚至可以启动100k OS线程吗?

看到答案后更新2:

如果GIL没有真正提供一种方法来执行简单的操作,例如原子递增,那么将它放在那里的目的是什么?它对于讨厌的并发问题没有帮助,那么它为什么要到位呢?我听说过C扩展的用例,有人会举例说明吗?

Ned*_*der 9

GIL同步字节码操作.一次只能执行一个字节代码.但是,如果您的操作需要多个字节码,则可以在字节码之间切换线程.如果您需要将操作设置为原子操作,那么您需要在GIL之上和之外进行同步.

例如,递增整数不是单个字节码:

>>> def f():
...   global num
...   num += 1
...
>>> dis.dis(f)
  3           0 LOAD_GLOBAL              0 (num)
              3 LOAD_CONST               1 (1)
              6 INPLACE_ADD
              7 STORE_GLOBAL             0 (num)
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)

这里花了四个字节码来实现num += 1.GIL不会确保x以原子方式递增.您的实验演示了问题:您丢失了更新,因为线程在LOAD_GLOBAL和STORE_GLOBAL之间切换.

GIL的目的是确保Python对象的引用计数以原子方式递增和递减.它并不是为了帮助您使用自己的数据结构.