文章首發微信公眾號,微信搜索:猿說python
對于線程與線程之間的交互我們在前面的文章已經介紹了 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 = 0def thread_consumers():# 條件變量condition 線程上鎖con.acquire()# 全局變量聲明關鍵字 globalglobal meat_nummeat_num = 0# 等待肉片下鍋煮熟con.wait()while True:print("我來一塊肉片...")meat_num -= 1print("剩余肉片數量:%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()# 全局變量聲明關鍵字 globalglobal meat_num# 肉片熟了,可以開始吃了meat_num = 10print("肉片熟了,可以開始吃了...")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可以用于比較復雜的線程交互!
猜你喜歡:
1.python線程創建和參數傳遞
2.python線程互斥鎖Lock
3.python線程事件Event
4.python return邏輯判斷表達式
轉載請注明:猿說Python ? python條件變量Condition
想了解更多python內容請直接搜索微信公眾號:猿說python
Python教程 - 猿說Python?www.shuopython.com