线程安全问题

线程安全问题

一、造成线程安全问题的原因:

GIL全局解释器锁

每个线程在执行时候都需要先获取GIL,保证同一时刻只有一个线程可以执行代码,即同一时刻只有一个线程使用CPU,也就是说python的多线程并不是真正意义上的同时执行。

image-20210723154134438

二、多线程共享全局变量

Python多线程是通过threading模块来实现的。

在一个进程内的所有线程共享全局变量,很方便在多个线程间共享数据。

from threading import Thread
 
list_a = [1, 2, 3]
 
def add_list():
    global list_a
    list_a.append(100)
    print(list_a)
 
if __name__ == '__main__':
    t1 = Thread(target=add_list)
    t2 = Thread(target=add_list)
    print(t1.name)
    t1.start()
    print(t2.name)
    t2.start()
    
Thread-1
[1, 2, 3, 100]
Thread-2
[1, 2, 3, 100, 100]

三、多线程的资源竞争问题(线程非安全)

在多个线程对全局变量进行修改时,造成得到的结果不正确,这种情况就是线程安全问题。

from threading import Thread

num = 0

def run1():
    global num
    for i in range(1000000):
        num += 1

def run2():
    global num
    for i in range(1000000):
        num += 1

if __name__ == '__main__':
    t1 = Thread(target=run1,name="t1")
    t2 = Thread(target=run2,name="t2")
    t1.start()
    t2.start()
    print(num)

运行结果 :748795  每次还都不相同

四、怎么解决多线程竞争问题?---最简单的同步机制是使用互斥锁

#	某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改。直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行操作,从而保证了多线程情况下数据的正确性。

*注意:
	多个线程使用的是同一个锁,如果数据没有被锁锁上,那么acquire()方法不会堵塞,会执行上锁操作。如果在调用acquire时,数据已经被其他线程上了锁,那么acquire()方法会堵塞,直到数据被解锁为止。
    
    
*上锁解锁过程:
"""
1、当一个线程调用锁的acquire()方法获得锁时,锁就进入“locked”状态。
2、每次只有一个线程可以获得锁。如果此时另一个线程试图获得这个锁,该线程就会变为“blocked”状态,称为“阻塞”,直到拥有锁的线程调用锁的release()方法释放锁之后,锁进入“unlocked”状态。
3、线程调度程序从处于同步阻塞状态的线程中选择一个来获得锁,并使得该线程进入运行(running)状态。
"""
from threading import Thread, Lock, enumerate
import time
 
 
num = 0
mutex = Lock()
 
 
def add_num():
    global num
    for i in range(100000):
        mutex.acquire()
        num += 1
        mutex.release()
 
 
if __name__ == '__main__':
    t1 = Thread(target=add_num)
    t2 = Thread(target=add_num)
    t3 = Thread(target=add_num)
    t1.start()
    t2.start()
    t3.start()
 
    while len(enumerate()) != 1:
        time.sleep(1)
    print(num)

五、死锁及解决方法

python中在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁,因为系统判断这部分资源都正在使用,所有这两个线程在无外力作用下将一直等待下去。

为了支持在同一线程中多次请求同一资源,python提供了"递归锁":threading.RLock。RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其他的线程才能获得资源。

作者:钱煜

posted @   中亿丰数字科技  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示