Python Thread虚假唤醒概念与防范详解

 更新时间:2023年02月28日 09:59:34   作者:狂飙-高启强-张颂文-王天龙  
这篇文章主要介绍了Python Thread虚假唤醒概念与防范,虚假唤醒是一种现象,它只会出现在多线程环境中,指的是在多线程环境下,多个线程等待在同一个条件上,等到条件满足时,所有等待的线程都被唤醒,但由于多个线程执行的顺序不同

什么是虚假唤醒

虚假唤醒是一种现象,它只会出现在多线程环境中,指的是在多线程环境下,多个线程等待在同一个条件上,等到条件满足时,所有等待的线程都被唤醒,但由于多个线程执行的顺序不同,后面竞争到锁的线程在获得时间片时条件已经不再满足,线程应该继续睡眠但是却继续往下运行的一种现象。

上面是比较书面化的定义,我们用人能听懂的话来介绍一下虚假唤醒。

多线程环境的编程中,我们经常遇到让多个线程等待在一个条件上,等到这个条件成立的时候我们再去唤醒这些线程,让它们接着往下执行代码的场景。假如某一时刻条件成立,所有的线程都被唤醒了,然后去竞争锁,因为同一时刻只会有一个线程能拿到锁,其他的线程都会阻塞到锁上无法往下执行,等到成功争抢到锁的线程消费完条件,释放了锁,后面的线程继续运行,拿到锁时这个条件很可能已经不满足了,这个时候线程应该继续在这个条件上阻塞下去,而不应该继续执行,如果继续执行了,就说发生了虚假唤醒。

import threading
from threading import Condition
class Data:
    def __init__(self, cond, num):
        self.num = num
        self.cond = cond
    def add(self):
        self.cond: Condition = self.cond
        self.cond.acquire()
        if self.num > 0:
            self.cond.wait()
        self.num += 1
        print(threading.current_thread().getName(), self.num)
        self.cond.notifyAll()
        self.cond.release()
    def decr(self):
        self.cond: Condition = self.cond
        self.cond.acquire()
        if self.num == 0:
            self.cond.wait()
        self.num -= 1
        print(threading.current_thread().getName(), self.num)
        self.cond.notifyAll()
        self.cond.release()
if __name__ == '__main__':
    cond = Condition()
    num = 0
    data = Data(cond, 0)
    thread_add = threading.Thread(name="A", target=data.add)
    thread_decr = threading.Thread(name="B", target=data.decr)
    thread_add.start()
    thread_decr.start()
 

现在改用4个线程

import threading
from threading import Condition
class Data:
    def __init__(self, cond, num):
        self.num = num
        self.cond = cond
    def add(self):
        self.cond: Condition = self.cond
        self.cond.acquire()
        if self.num > 0:
            self.cond.wait()
        self.num += 1
        print(threading.current_thread().getName(), self.num)
        self.cond.notifyAll()
        self.cond.release()
    def decr(self):
        self.cond: Condition = self.cond
        self.cond.acquire()
        if self.num == 0:
            self.cond.wait()
        self.num -= 1
        print(threading.current_thread().getName(), self.num)
        self.cond.notifyAll()
        self.cond.release()
if __name__ == '__main__':
    cond = Condition()
    num = 0
    data = Data(cond, 0)
    thread_add = threading.Thread(name="A", target=data.add)
    thread_decr = threading.Thread(name="B", target=data.decr)
    thread_add2 = threading.Thread(name="C", target=data.add)
    thread_decr2 = threading.Thread(name="D", target=data.decr)
    thread_add.start()
    thread_decr.start()
    thread_add2.start()
    thread_decr2.start()

还没有出现问题!!!

使用20个线程同时跑

import threading
from threading import Condition
class Data:
    def __init__(self, cond, num):
        self.num = num
        self.cond = cond
    def add(self):
        self.cond: Condition = self.cond
        self.cond.acquire()
        if self.num > 0:
            self.cond.wait()
        self.num += 1
        print(threading.current_thread().getName(), self.num)
        self.cond.notifyAll()
        self.cond.release()
    def decr(self):
        self.cond: Condition = self.cond
        self.cond.acquire()
        if self.num == 0:
            self.cond.wait()
        self.num -= 1
        print(threading.current_thread().getName(), self.num)
        self.cond.notifyAll()
        self.cond.release()
if __name__ == '__main__':
    cond = Condition()
    num = 0
    data = Data(cond, 0)
    for i in range(10):
        thread_add = threading.Thread(name="A", target=data.add)
        thread_add.start()
    for i in range(10):
        thread_decr = threading.Thread(name="B", target=data.decr)
        thread_decr.start()

这时就出现了问题!!!

现在改用while进行判断

防止虚假唤醒:

import threading
from threading import Condition
class Data:
    def __init__(self, cond, num):
        self.num = num
        self.cond = cond
    def add(self):
        self.cond: Condition = self.cond
        self.cond.acquire()
        # 这里采用了while进行判断,防止虚假唤醒
        while self.num > 0:
            self.cond.wait()
        self.num += 1
        print(threading.current_thread().getName(), self.num)
        self.cond.notifyAll()
        self.cond.release()
    def decr(self):
        self.cond: Condition = self.cond
        self.cond.acquire()
        # 这里采用了while进行判断,防止虚假唤醒
        while self.num == 0:
            self.cond.wait()
        self.num -= 1
        print(threading.current_thread().getName(), self.num)
        self.cond.notifyAll()
        self.cond.release()
if __name__ == '__main__':
    cond = Condition()
    num = 0
    data = Data(cond, 0)
    for i in range(10):
        thread_add = threading.Thread(name="A", target=data.add)
        thread_add.start()
    for i in range(10):
        thread_decr = threading.Thread(name="B", target=data.decr)
        thread_decr.start()

这个例子与上面的代码几乎没有差别,只是把if判断换成了while判断,所以每次萧炎和唐三醒过来之后都会再判断一下有没有苹果(唤醒自己的条件是否满足),如果不满足,就会继续睡下去,不会接着往下运行,从而避免了虚假唤醒。

总结

等待在一个条件上的线程被全部唤醒后会去竞争锁,所以这些线程会一个一个地去消费这个条件,等到后面的线程去消费这个条件时,条件可能已经不满足了,所以每个被唤醒的线程都需要再检查一次条件是否满足。如果不满足,应该继续睡下去;只有满足了才能往下执行。

到此这篇关于Python Thread虚假唤醒概念与防范详解的文章就介绍到这了,更多相关Python Thread虚假唤醒内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 对python3 一组数值的归一化处理方法详解

    对python3 一组数值的归一化处理方法详解

    今天小编就为大家分享一篇对python3 一组数值的归一化处理方法详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07
  • Django自关联实现多级联动查询实例

    Django自关联实现多级联动查询实例

    这篇文章主要介绍了Django自关联实现多级联动查询实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-05-05
  • Python中文分词实现方法(安装pymmseg)

    Python中文分词实现方法(安装pymmseg)

    这篇文章主要介绍了Python中文分词实现方法,通过安装pymmseg来实现分词功能,涉及pymmseg的下载、解压、安装及使用技巧,需要的朋友可以参考下
    2016-06-06
  • 利用python-pypcap抓取带VLAN标签的数据包方法

    利用python-pypcap抓取带VLAN标签的数据包方法

    今天小编就为大家分享一篇利用python-pypcap抓取带VLAN标签的数据包方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-07-07
  • python实现计算倒数的方法

    python实现计算倒数的方法

    这篇文章主要介绍了python实现计算倒数的方法,涉及Python针对数学运算操作的相关技巧,需要的朋友可以参考下
    2015-07-07
  • pandas.read_csv参数详解(小结)

    pandas.read_csv参数详解(小结)

    这篇文章主要介绍了pandas.read_csv参数详解(小结),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-06-06
  • django框架forms组件用法实例详解

    django框架forms组件用法实例详解

    这篇文章主要介绍了django框架forms组件用法,结合实例形式详细分析了Django框架forms组件源码及常用操作方法与使用注意事项,需要的朋友可以参考下
    2019-12-12
  • Python通过yagmail实现发送邮件代码解析

    Python通过yagmail实现发送邮件代码解析

    这篇文章主要介绍了Python通过yagmail实现发送邮件代码解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • 利用Pandas读取某列某行数据之loc和iloc用法总结

    利用Pandas读取某列某行数据之loc和iloc用法总结

    loc是location的意思,和iloc中i的意思是指integer,所以它只接受整数作为参数,下面这篇文章主要给大家介绍了关于利用Pandas读取某列某行数据之loc和iloc用法的相关资料,需要的朋友可以参考下
    2022-03-03
  • Python实现动态条形图绘制的示例代码

    Python实现动态条形图绘制的示例代码

    这篇文章主要为大家详细介绍了如何利用Python语言实现动态条形图的绘制,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2022-08-08

最新评论