Python 中 GIL 的新实现是否处理了竞争条件问题?

2023-11-21

我读了一篇文章关于 Python 中的多线程,他们尝试使用同步来解决竞争条件问题。我运行了下面的示例代码来重现竞争条件问题:

import threading 

# global variable x 
x = 0

def increment(): 
    """ 
    function to increment global variable x 
    """
    global x 
    x += 1

def thread_task(): 
    """ 
    task for thread 
    calls increment function 100000 times. 
    """
    for _ in range(100000): 
        increment() 

def main_task(): 
    global x 
    # setting global variable x as 0 
    x = 0

    # creating threads 
    t1 = threading.Thread(target=thread_task) 
    t2 = threading.Thread(target=thread_task) 

    # start threads 
    t1.start() 
    t2.start() 

    # wait until threads finish their job 
    t1.join() 
    t2.join() 

if __name__ == "__main__": 
    for i in range(10): 
        main_task() 
        print("Iteration {0}: x = {1}".format(i,x)) 

当我使用 Python 2.7.15 时,它确实返回与文章相同的结果。但当我使用 Python 3.6.9 时却不会(所有线程返回相同的结果 = 200000)。

我想知道 GIL 的新实现(自 Python 3.2 起)是否处理​​了竞争条件问题?如果是的话,为什么 Lock、Mutex 仍然存在于 Python >3.2 中。如果没有,为什么像上面的例子那样运行多线程修改共享资源时不会发生冲突?

这些天,当我试图更多地了解 Python 的底层工作原理时,我的脑海里一直在思考这些问题。


您所指的更改是将检查间隔替换为切换间隔。这意味着不是每 100 个字节代码切换一次线程,而是每 5 毫秒切换一次。

Ref: https://pymotw.com/3/sys/threads.html https://mail.python.org/pipermail/python-dev/2009-October/093321.html

因此,如果您的代码运行得足够快,它就永远不会经历线程切换,并且您可能会觉得这些操作是原子的,但实际上并非如此。由于没有实际的线程交织,因此没有出现竞争条件。x += 1实际上是四个字节码:

>>> dis.dis(sync.increment)
 11           0 LOAD_GLOBAL              0 (x)
              3 LOAD_CONST               1 (1)
              6 INPLACE_ADD         
              7 STORE_GLOBAL             0 (x)
             10 LOAD_CONST               2 (None)
             13 RETURN_VALUE        

解释器中的线程切换可以发生在任意两个字节码之间。

考虑一下,在 2.7 中,它总是打印 200000,因为检查间隔设置得太高,以至于每个线程在下一个线程运行之前完整完成。同样可以用开关间隔来构造。

import sys
import threading 

print(sys.getcheckinterval())
sys.setcheckinterval(1000000)

# global variable x 
x = 0

def increment(): 
    """ 
    function to increment global variable x 
    """
    global x 
    x += 1

def thread_task(): 
    """ 
    task for thread 
    calls increment function 100000 times. 
    """
    for _ in range(100000): 
        increment() 

def main_task(): 
    global x 
    # setting global variable x as 0 
    x = 0

    # creating threads 
    t1 = threading.Thread(target=thread_task) 
    t2 = threading.Thread(target=thread_task) 

    # start threads 
    t1.start() 
    t2.start() 

    # wait until threads finish their job 
    t1.join() 
    t2.join() 

if __name__ == "__main__": 
    for i in range(10): 
        main_task() 
        print("Iteration {0}: x = {1}".format(i,x)) 
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python 中 GIL 的新实现是否处理了竞争条件问题? 的相关文章

随机推荐