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

目录
  • 什么是虚假唤醒
  • 现在改用4个线程
  • 使用20个线程同时跑
  • 现在改用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()
        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虚假唤醒内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • python 包之 threading 多线程

    目录 一.创建一个线程 二.创建多个线程 三.线程同步 四.递归锁 五.信号锁 一.创建一个线程 通过实例化threading.Thread类创建线程 import threading def func(s): print(s) if __name__ == '__main__': # 创建线程 thread = threading.Thread(target=func, args=('hello',)) # 启动线程 thread.start() # 等待线程结束 thread.join()

  • Python线程编程之Thread详解

    目录 一.线程编程(Thread) 1.线程基本概念 1.1.什么事线程 1.2.线程特征 二.threading模块创建线程 1.创建线程对象 2. 启动线程 3. 回收线程 4.代码演示 5.线程对象属性 6.自定义线程类 7.一个很重要的练习 我很多不懂 8.线程间通信 1. 线程Event 代码演示 2. 线程锁 Lock代码演示 10.死锁及其处理 1.定义 2.图解 3. 死锁产生条件 4.死锁代码演示 python线程GIL 1.python线程的GIL问题 (全局解释器锁) 总结

  • python 多线程threading程序详情

    CPython implementation detail: 在 CPython 中,由于存在全局解释器锁, 同一时刻只有一个线程可以执行 Python 代码(虽然某些性能导向的库可能会去除此限制). 如果你想让你的应用更好地利用多核心计算机的计算资源,推荐你使用multiprocessing或concurrent.futures.ProcessPoolExecutor但是,如果你想要同时运行多个 I/O 密集型任务,则多线程仍然是一个合适的模型. 再来引入一个概念: 并行(parallelis

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

    目录 什么是虚假唤醒 现在改用4个线程 使用20个线程同时跑 现在改用while进行判断 总结 什么是虚假唤醒 虚假唤醒是一种现象,它只会出现在多线程环境中,指的是在多线程环境下,多个线程等待在同一个条件上,等到条件满足时,所有等待的线程都被唤醒,但由于多个线程执行的顺序不同,后面竞争到锁的线程在获得时间片时条件已经不再满足,线程应该继续睡眠但是却继续往下运行的一种现象. 上面是比较书面化的定义,我们用人能听懂的话来介绍一下虚假唤醒. 多线程环境的编程中,我们经常遇到让多个线程等待在一个条件上,

  • python压包的概念及实例详解

    对于一些分解后的元素,我们也是有重新归类的需要.那么我们把解包的恢复过程,叫做压包.这里要用到zip函数的方法,对元素重新进行打包处理,在之前的学习中我们已经对zip函数有所接触.下面我们就python压包的概念.方法进行介绍,然后带来相关的实例使用. 1.概念 压包是解包的逆过程,用zip函数实现. 2.方法 (1)zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的对象(Python3). (2)如果各个迭代器的元素个数不一致,则返回列表长

  • Python元组 tuple的概念与基本操作详解【定义、创建、访问、计数、推导式等】

    本文实例讲述了Python元组 tuple的概念与基本操作.分享给大家供大家参考,具体如下: 元组 tuple 元组 tuple的定义 元组的创建 元组的元素访问和计数 1. 元组的元素不能修改 2. 元组的元素访问和列表一样,只不过返回的仍然是元组对象. 3. 列表关于排序的方法 list.sorted()是修改原列表对象,元组没有该方法.如果要对元组排 zip 生成器推导式创建元组 元组总结 元组 tuple的定义 列表属于可变序列,可以任意修改列表中的元素.元组属于不可变序列,不能修改元组

  • Python装饰器基础概念与用法详解

    本文实例讲述了Python装饰器基础概念与用法.分享给大家供大家参考,具体如下: 装饰器基础 前面快速介绍了装饰器的语法,在这里,我们将深入装饰器内部工作机制,更详细更系统地介绍装饰器的内容,并学习自己编写新的装饰器的更多高级语法. 什么是装饰器 装饰是为函数和类指定管理代码的一种方式.Python装饰器以两种形式呈现: [1]函数装饰器在函数定义的时候进行名称重绑定,提供一个逻辑层来管理函数和方法或随后对它们的调用. [2]类装饰器在类定义的时候进行名称重绑定,提供一个逻辑层来管理类,或管理随

  • Python实现定时任务的八种方案详解

    目录 利用whileTrue:+sleep()实现定时任务 使用Timeloop库运行定时任务 利用threading.Timer实现定时任务 利用内置模块sched实现定时任务 利用调度模块schedule实现定时任务 利用任务框架APScheduler实现定时任务 APScheduler中的重要概念 Job作业 Trigger触发器 Executor执行器 Jobstore作业存储 Event事件 调度器 Scheduler的工作流程 使用分布式消息系统Celery实现定时任务 使用数据流工

  • Python中lru_cache的使用和实现详解

    在计算机软件领域,缓存(Cache)指的是将部分数据存储在内存中,以便下次能够更快地访问这些数据,这也是一个典型的用空间换时间的例子.一般用于缓存的内存空间是固定的,当有更多的数据需要缓存的时候,需要将已缓存的部分数据清除后再将新的缓存数据放进去.需要清除哪些数据,就涉及到了缓存置换的策略,LRU(Least Recently Used,最近最少使用)是很常见的一个,也是 Python 中提供的缓存置换策略. 下面我们通过一个简单的示例来看 Python 中的 lru_cache 是如何使用的.

  • 详解Python中的GIL(全局解释器锁)详解及解决GIL的几种方案

    先看一道GIL面试题: 描述Python GIL的概念, 以及它对python多线程的影响?编写一个多线程抓取网页的程序,并阐明多线程抓取程序是否可比单线程性能有提升,并解释原因. GIL:又叫全局解释器锁,每个线程在执行的过程中都需要先获取GIL,保证同一时刻只有一个线程在运行,目的是解决多线程同时竞争程序中的全局变量而出现的线程安全问题.它并不是python语言的特性,仅仅是由于历史的原因在CPython解释器中难以移除,因为python语言运行环境大部分默认在CPython解释器中. 通过

  • Python并发编程线程消息通信机制详解

    目录 1 Event事件 2 Condition 3 Queue队列 4 总结一下 前面我已经向大家介绍了,如何使用创建线程,启动线程.相信大家都会有这样一个想法,线程无非就是创建一下,然后再start()下,实在是太简单了. 可是要知道,在真实的项目中,实际场景可要我们举的例子要复杂的多得多,不同线程的执行可能是有顺序的,或者说他们的执行是有条件的,是要受控制的.如果仅仅依靠前面学的那点浅薄的知识,是远远不够的. 那今天,我们就来探讨一下如何控制线程的触发执行. 要实现对多个线程进行控制,其实

  • 基于Python对象引用、可变性和垃圾回收详解

    变量不是盒子 在示例所示的交互式控制台中,无法使用"变量是盒子"做解释.图说明了在 Python 中为什么不能使用盒子比喻,而便利贴则指出了变量的正确工作方式. 变量 a 和 b 引用同一个列表,而不是那个列表的副本 >>> a = [1, 2, 3] >>> b = a >>> a.append(4) >>> b [1, 2, 3, 4] 如果把变量想象为盒子,那么无法解释 Python 中的赋值:应该把变量视作

  • Python编程使用NLTK进行自然语言处理详解

    自然语言处理是计算机科学领域与人工智能领域中的一个重要方向.自然语言工具箱(NLTK,NaturalLanguageToolkit)是一个基于Python语言的类库,它也是当前最为流行的自然语言编程与开发工具.在进行自然语言处理研究和应用时,恰当利用NLTK中提供的函数可以大幅度地提高效率.本文就将通过一些实例来向读者介绍NLTK的使用. NLTK NaturalLanguageToolkit,自然语言处理工具包,在NLP领域中,最常使用的一个Python库. NLTK是一个开源的项目,包含:P

随机推荐