多线程并发--竞争问题

前言: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()

题目出处:https://leetcode-cn.com/problems/print-in-order/

posted @ 2020-08-10 14:39  Lei、Sunny  阅读(692)  评论(0编辑  收藏  举报