前言:GIL
GIL的全称是Global Interpreter Lock(全局解释器锁),来源是python设计之初的考虑,为了数据安全所做的决定。某个线程想要执行,必须先拿到GIL,我们可以把GIL看作是“通行证”,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。GIL只在cpython中才有,因为cpython调用的是c语言的原生线程,所以他不能直接操作cpu,只能利用GIL保证同一时间只能有一个线程拿到数据。而在pypy和jpython中是没有GIL的。
在python中多线程的并发只能每一刻执行一个线程,那么就会产生一个竞争资源问题,线程是共享数据的。
在这用力扣的题目解释用同步锁机制解决竞争条件
假设有一个方法 withdraw(amount)
,如果请求量小于当前余额,则从当前余额中减去请求量,然后返回余额。方法定义如下:
1 balance = 500 2 def withdraw(amount): 3 if (amount < balance): 4 balance -= amount 5 return balance
正常结果余额不能为负。但此时我用两个线程不同参数执行该方法时,如:线程1 withdraw(400)和线程2 withdraw(200)的执行流程如下图:
对于这种问题,我们可以利用锁来解决,将一个线程的关键执行部分加上锁,执行完在释放。如下图
在3、4步的时候,线程2执行到来关键部分所以加上来锁,因此线程1此时处于休眠状态,5、6步的时候同理。
例题:--同步锁
三个不同的线程将会共用一个 Foo 实例。
线程 A 将会调用 one() 方法
线程 B 将会调用 two() 方法
线程 C 将会调用 three() 方法
请设计修改程序,以确保 two() 方法在 one() 方法之后被执行,three() 方法在 two() 方法之后被执行。
思路:利用依赖关系,创建一个变量firstJobDone协调one()和two()之间的执行顺序,在创建一个变量secondJobDone协调two()和three()之间的执行关系
- 首先初始化共享变量 firstJobDone 和 secondJobDone,初始值表示所有方法未执行。
- 方法 first() 没有依赖关系,可以直接执行。在方法最后更新变量 firstJobDone 表示该方法执行完成。
- 方法 second() 中,检查 firstJobDone 的状态。如果未更新则进入等待状态,否则执行方法 second()。在方法末尾,更新变量 secondJobDone 表示方法 second() 执行完成。
- 方法 third() 中,检查 secondJobDone 的状态。与方法 second() 类似,执行 third() 之前,需要先等待 secondJobDone 的状态。
from threading import Lock class Foo: def __init__(self): self.firstjobDone = Lock() self.secondjobDone = Lock() self.firstjobDone.acquire() # 请求加锁 self.secondjobDone.acquire() def first(self, printFirst: 'Callable[[], None]') -> None: # printFirst() outputs "first". Do not change or remove this line. printFirst() self.firstjobDone.release() # 释放锁 def second(self, printSecond: 'Callable[[], None]') -> None: # printSecond() outputs "second". Do not change or remove this line. with self.firstjobDone: # 上下文管理器,可获取和释放锁 printSecond() self.secondjobDone.release() def third(self, printThird: 'Callable[[], None]') -> None: # printThird() outputs "third". Do not change or remove this line. with self.secondjobDone: printThird()