32、互斥锁(同步锁)
一、互斥(同步)锁
互斥锁又称同步锁,而锁的目的是为了保护共享的数据,同一时间只能有一个线程来修改共享的数据
那我们之前学过GIL锁,那GIL锁和互斥锁不同之处在哪?
1、GIL锁和Lock
前者是解释器级别的(当然保护的就是解释器级别的数据,比如垃圾回收的数据),后者是保护用户自己开发的应用程序的数据,很明显GIL不负责这件事,只能用户自定义加锁处理,即Lock
总结:线程抢的是GIL锁,GIL锁相当于执行权限,拿到执行权限后才能拿到互斥锁Lock,其他线程也可以抢到GIL,但如果发现Lock仍然没有被释放则阻塞,即便是拿到执行权限GIL也要立刻交出来
2、为何要用互斥锁
举个例子:线程1抢到GIL锁,拿到执行权限,开始执行,然后加了一把Lock,还没有执行完毕,即线程1还未释放Lock,有可能线程2抢到GIL锁,开始执行,执行过程中发现Lock还没有被线程1释放,于是线程2进入阻塞,被夺走执行权限,然后线程3继续接着上面的骚操作。。。这就导致了串行运行的效果,这种情况通常是因为IO操作或者时间片到了导致,我们用一段代码模拟一下吧
from threading import Thread
import time
def work():
global n
temp = n
time.sleep(0.1) # 模拟IO操作让cpu切换一下
n = temp - 1
if __name__ == '__main__':
n = 100
l = []
for i in range(100): # 起了一百个线程
p = Thread(target=work)
l.append(p)
p.start()
for p in l:
p.join()
print(n) # 结果可能为99
可以看出,定义了一个n=100的变量,然后启动了一百个线程,每次都做了n-1的操作,正常来讲最后的结果应该是等于0,这里我们就用time暂停0.1秒模拟一下IO操作让线程停顿一下,结果就导致了线程1还没把n减1呢,线程2就抢夺了权限...停顿...又被线程3...一直到第100线程......
结果就会导致这些线程拿到的n全是100,然后减的1就变成99了。
3、如何使用互斥锁
临界区加锁(多个线程操作出乱的区间)
from threading import Thread, Lock # 导入互斥锁
import time
def work():
global n
lock.acquire() # 加锁
temp = n
time.sleep(0.1)
n = temp - 1
lock.release() # 释放锁
if __name__ == '__main__':
lock = Lock() # 实例化锁
n = 100
l = []
for i in range(100):
p = Thread(target=work)
l.append(p)
p.start()
for p in l:
p.join()
# 结果肯定为0,由原来的并发执行变成串行,牺牲了执行效率保证了数据安全
print(n)
二、信号量
信号量可以理解为多把锁,同时允许多个线程来更改数据。这个场景比较少见
比如说我们有一家食堂,只有两个窗口取餐,后面想取餐的需要等前面取完餐才可以取餐
from threading import Thread, Semaphore
import time
import random
sm = Semaphore(2) # 设置两个窗口
def task(name):
sm.acquire() # 上锁
print('%s正在取餐' % name)
time.sleep(random.randint(1, 3))
print('%s取完了' % name)
sm.release() # 释放锁
if __name__ == '__main__':
for i in range(1, 11): # 设置十个人排队取餐
t = Thread(target=task, args=(i,))
t.start()
三、Event事件
Event 是用来实现 进程 或 线程 之间同步通信的,我们通过下面一个例子来了解一下Event的作用
from threading import Thread, Event
import time
event = Event()
def girl(name):
print('%s 还在谈着恋爱' % name)
time.sleep(3)
event.set() # 发信号
print('%s 单身了' % name)
def boy(name):
print('%s 等着女神分手' % name)
event.wait()
print('女神分手了,开始追')
if __name__ == '__main__':
t = Thread(target=girl, args=('刘亦菲',))
t.start()
for i in range(5):
t = Thread(target=boy, args=('屌丝%s号' % i,))
t.start()
本文作者:黑影Poco
本文链接:https://www.cnblogs.com/poco/p/15046140.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步