对于线程与线程之间的交互我们在前面的文章已经介绍了 python 互斥锁Lock / python事件Event , 今天继续介绍一种线程交互方式 – 线程条件变量Condition.
一.线程条件变量Condition相关函数介绍
acquire() — 线程锁,注意线程条件变量Condition中的所有相关函数使用必须在acquire() /release() 内部操作;
release() — 释放锁,注意线程条件变量Condition中的所有相关函数使用必须在acquire() /release() 内部操作;
wait(timeout) — 线程挂起(阻塞状态),直到收到一个notify通知或者超时才会被唤醒继续运行(超时参数默认不设置,可选填,类型是浮点数,单位是秒)。wait()必须在已获得Lock前提下才能调用,否则会触发RuntimeError;
notify(n=1) — 通知其他线程,那些挂起的线程接到这个通知之后会开始运行,缺省参数,默认是通知一个正等待通知的线程,最多则唤醒n个等待的线程。notify()必须在已获得Lock前提下才能调用,否则会触发RuntimeError,notify()不会主动释放Lock;
notifyAll() — 如果wait状态线程比较多,notifyAll的作用就是通知所有线程;
二.线程条件变量Condition原理
在前面的文章已经介绍过互斥锁,主要作用是并行访问共享资源时,保护共享资源,防止出现脏数据。python 条件变量Condition也需要关联互斥锁,同时Condition自身提供了wait/notify/notifyAll方法,用于阻塞/通知其他并行线程,可以访问共享资源了。可以这么理解,Condition提供了一种多线程通信机制,假如线程1需要数据,那么线程1就阻塞等待,这时线程2就去制造数据,线程2制造好数据后,通知线程1可以去取数据了,然后线程1去获取数据。
三.线程条件变量Condition使用
案例一:成语接龙
# !usr/bin/env python # -*- coding:utf-8 _*- """ @Author:何以解忧 @Blog(个人博客地址): shuopython.com @WeChat Official Account(微信公众号):猿说python @Github:www.github.com @File:python_.py @Time:2019/10/21 21:25 @Motto:不积跬步无以至千里,不积小流无以成江海,程序人生的精彩需要坚持不懈地积累! """ # 导入线程模块 import threading # 创建条件变量condition con = threading.Condition() def thread_one(name): # 条件变量condition 线程上锁 con.acquire() print("{}:成语接龙准备好了吗".format(name)) # 唤醒正在等待(wait)的线程 con.notify() # 等待对方回应消息,使用wait阻塞线程,等待对方通过notify唤醒本线程 con.wait() print("{}:一干二净".format(name)) # 唤醒对方 con.notify() # 等待消息答应 con.wait() print("{}:一天就知道看抖音美女,给你来个简单点的,来了:毛手毛脚".format(name)) # 唤醒对方 con.notify() # 等待消息答应 con.wait() print("{}:哟哟哟,不错不错!".format(name)) # 唤醒对方 con.notify() # 条件变量condition 线程释放锁 con.release() def thread_two(name): # 条件变量condition 线程上锁 con.acquire() # wait阻塞状态,等待其他线程通过notify唤醒本线程 con.wait() print("{}:准备好了~开始吧!".format(name)) # 唤醒对方 con.notify() # 等待消息答应 con.wait() print("{}:净你妹啊,没法接...来个简单点的...".format(name)) # 唤醒对方 con.notify() # 等待消息答应 con.wait() print("{}:嘿,这个我知道:脚踏实地".format(name)) # 唤醒对方 con.notify() con.release() if __name__ == "__main__": # 创建并初始化线程 t1 = threading.Thread(target=thread_one,args=("A")) t2 = threading.Thread(target=thread_two,args=("B")) # 启动线程 -- 注意线程启动顺序,启动顺序很重要 t2.start() t1.start() # 阻塞主线程,等待子线程结束 t1.join() t2.join() print("程序结束!")
输出结果:
A:成语接龙准备好了吗 B:准备好了~开始吧! A:一干二净 B:净你妹啊,没法接...来个简单点的... A:一天就知道看抖音美女,给你来个简单点的,来了:毛手毛脚 B:嘿,这个我知道:脚踏实地 A:哟哟哟,不错不错! 程序结束!
案例二:生产者与消费者模式,以吃火锅为例:一盘老肉片有10块肉,吃完了又重新往锅里加….
生产者:往锅里加老肉片,每次加一盘(10块);
消费者:吃煮熟的肉片,没吃一片,肉片数量减一,吃完为止;
# 导入线程模块 import threading import time # 创建条件变量condition con = threading.Condition() meat_num = 0 def thread_consumers(): # 条件变量condition 线程上锁 con.acquire() # 全局变量声明关键字 global global meat_num meat_num = 0 # 等待肉片下锅煮熟 con.wait() while True: print("我来一块肉片...") meat_num -= 1 print("剩余肉片数量:%d"%meat_num) time.sleep(0.5) if meat_num == 0: # 肉片吃光了,通知老板添加肉片 print("老板,再来一份老肉片...") con.notify() # 肉片吃光了,等待肉片 con.wait() # 条件变量condition 线程释放锁 con.release() def thread_producer(): # 条件变量condition 线程上锁 con.acquire() # 全局变量声明关键字 global global meat_num # 肉片熟了,可以开始吃了 meat_num = 10 print("肉片熟了,可以开始吃了...") con.notify() while True: # 阻塞函数,等待肉片吃完的通知 con.wait() meat_num = 10 # 添加肉片完成,可以继续开吃 print("添加肉片成功!当前肉片数量:%d"%meat_num) time.sleep(1) con.notify() con.release() if __name__ == "__main__": # 创建并初始化线程 t1 = threading.Thread(target=thread_producer) t2 = threading.Thread(target=thread_consumers) # 启动线程 -- 注意线程启动顺序,启动顺序很重要 t2.start() t1.start() # 阻塞主线程,等待子线程结束 t1.join() t2.join() print("程序结束!")
输出结果:
肉片熟了,可以开始吃了... 我来一块肉片... 剩余肉片数量:9 我来一块肉片... 剩余肉片数量:8 我来一块肉片... 剩余肉片数量:7 我来一块肉片... 剩余肉片数量:6 我来一块肉片... 剩余肉片数量:5 我来一块肉片... 剩余肉片数量:4 我来一块肉片... 剩余肉片数量:3 我来一块肉片... 剩余肉片数量:2 我来一块肉片... 剩余肉片数量:1 我来一块肉片... 剩余肉片数量:0 老板,再来一份老肉片... 添加肉片成功!当前肉片数量:10 我来一块肉片... 剩余肉片数量:9 我来一块肉片... 剩余肉片数量:8 我来一块肉片... 剩余肉片数量:7 .............
注意:
1.全局变量要声明关键字 global;
2.注意线程的启动顺序,这个很重要;
四.重点总结
注意线程互斥锁Lock/线程事件Event/线程条件变量Condition三者的区别,场景不同,使用方式也不同,前两者一般可以作为简单的线程交互,线程条件变量Condition可以用于比较复杂的线程交互!
猜你喜欢:
转载请注明:猿说Python » python条件变量Condition