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 中国大陆许可协议进行许可。

posted @   黑影Poco  阅读(477)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.