學習視頻鏈接: python語言愛好者的個人空間_哔哩哔哩_Bilibili P70、P71、P72
信號量:是最古老的同步原語之一,是一個計數器。當資源釋放時計數器就會遞增,當資源申請時計數器就會遞減。可以認為信號量就代表著資源是否可用
使用方法:
"""信號量"""
import threading
import time
def run(n,x):
semaphore.acquire() # 對該線程上鎖
print(n)
time.sleep(x) # 延遲1秒
semaphore.release() # 釋放鎖資源
if __name__ == '__main__':
semaphore = threading.Semaphore(3) # 將信號量實例化 括號裡填寫同時執行線程的個數
for i in range(50):
t = threading.Thread(target=run, args=(i, i)) # 調用threading.Thread()函數 target=方法名 args(參數, ) 如果沒有參數,則threading.Thread(target=方法名)即可
#args=(i, i) 中的參數對應 “target=方法名” 中方法的參數
t.start() # 線程執行語句
from threading import BoundedSemaphore
MAX = 3 # 資源最大值
semaphore = BoundedSemaphore(MAX)
print(semaphore._value)
semaphore.acquire()
print(semaphore._value)
semaphore.acquire()
print(semaphore._value)
semaphore.acquire()
print(semaphore._value) # 0
semaphore.acquire(False) # 沒有資源可以申請,value的值為0,再次調用acquire方法會被阻塞。參數設為False則返回False不會被阻塞
print(semaphore._value)
semaphore.release() #第一次釋放
print(semaphore._value)
semaphore.release()
print(semaphore._value)
semaphore.release() #第三次釋放
print(semaphore._value)
semaphore.release() #第四次釋放,由於現在已經沒有資源被占用所以會拋出異常
條件變量:由於有些復雜問題互斥鎖搞不定了。Python
提供的Condition
對象提供了對復雜線程同步問題的支持。Condition
被稱為條件變量,除了提供與Lock
類似的acquire
和release
方法外,還提供了wait
和notify
方法。
定義參考:[python] 線程間同步之條件變量Condition - 簡書
"""條件變量"""
import threading
import time
def run(x):
#lock.acquire()
con.acquire()#使用條件變量
print(f'線程{x}')
con.notify() #結束上一個進程的等待狀態 啟動上一個進程
print(f'線程{x}掛起')
con.wait()#條件變量中的等待,線程中的等待函數為join()
time.sleep(1)
print(f'線程{x}再次啟動')
con.notify()#程序運行完之後 啟動上一個線程
con.release()#資源釋放
#lock.release()
def run2(x):
#lock.acquire()
con.acquire()#使用條件變量
print(f'線程{x}')
con.notify() #結束上一個進程的等待狀態 啟動上一個進程
print(f'線程{x}掛起')
con.wait()#條件變量中的等待,線程中的等待函數為join()
time.sleep(1)
print(f'線程{x}再次啟動')
con.notify()#程序運行完之後 啟動上一個線程
con.release()#資源釋放
#lock.release()
if __name__=='__main__':
#lock = threading.Lock()
con = threading.Condition()#條件變量
# for i in range(10):
# t = threading.Thread(target=run,args=(i, ))
# t.start()
t1 = threading.Thread(target=run, args=(1,))
t1.start()
t2 = threading.Thread(target=run, args=(2,))
t2.start()
acquire([timeout])/release(): 調用關聯的鎖的相應方法。
wait([timeout]): 調用這個方法將使線程進入Condition的等待池等待通知,並釋放鎖。
使用前線程必須已獲得鎖定,否則將拋出異常。
notify(): 調用這個方法將從等待池挑選一個線程並通知,收到通知的線程將自動調用
acquire()嘗試獲得鎖定(進入鎖定池);其他線程仍然在等待池中。調用這個方法不會
釋放鎖定。使用前線程必須已獲得鎖定,否則將拋出異常。
notifyAll(): 調用這個方法將通知等待池中所有的線程,這些線程都將進入鎖定池
嘗試獲得鎖定。調用這個方法不會釋放鎖定。使用前線程必須已獲得鎖定,否則將拋出異常。
同進程的一樣,線程的一個關鍵特性是每個線程都是獨立運行且狀態不可預測。如果程序中的其 他線程需要通過判斷某個線程的狀態來確定自己下一步的操作,這時線程同步問題就會變得非常棘手。為了解決這些問題,我們需要使用threading庫中的Event對象。 對象包含一個可由線程設置的信號標志,它允許線程等待某些事件的發生。在 初始情況下,Event對象中的信號標志被設置為假。如果有線程等待一個Event對象, 而這個Event對象的標志為假,那麼這個線程將會被一直阻塞直至該標志為真。一個線程如果將一個Event對象的信號標志設置為真,它將喚醒所有等待這個Event對象的線程。如果一個線程等待一個已經被設置為真的Event對象,那麼它將忽略這個事件, 繼續執行。
"""事件"""
import threading
import time
def car():
while True:
if event.is_set():#如果設置了事件
print("小車行駛")
else:
print("小車停止")
event.wait()
def set_event():
while True:
event.set()#設置事件
time.sleep(1)
event.clear() #把所設置的事件清除
time.sleep(3)
if __name__ =='__main__':
event = threading.Event() #創建事件
car1 = threading.Thread(target=car)
car1.start()
set_e = threading.Thread(target=set_event)
set_e.start()
Event的幾種方法:
threading.Event(): 創建事件
event.isSet():返回event的狀態值;
event.wait():如果 event.isSet()==False將阻塞線程;
event.set(): 設置event的狀態值為True,所有阻塞池的線程激活進入就緒狀態, 等待操作系統調度;
event.clear():恢復event的狀態值為False。
本次任務:使用類繼承的方式,實現信號量、事件功能操作。具體案例:第一個線程中獲取當前時間,判斷當前時間3秒之後,觸發“事件”對象。在另一個線程中,作為數學考試結束的判斷變量,否則一直處於考試中,並打印
import threading
import time
def shijian():
while True:
day1=time.strftime("%Y %m %d %H:%M:%S", time.localtime())# 獲取 格式化成2016-03-20 11:45:39形式 的本地時間 並賦值給day1
day11=time.mktime(time.strptime(day1,"%Y %m %d %H:%M:%S"))# 將格式字符串轉換為時間戳 並賦值給day11
time.sleep(3)#延遲3秒
day2=time.strftime("%Y %m %d %H:%M:%S", time.localtime())# 獲取 格式化成2016-03-20 11:45:39形式 的本地時間 並賦值給day2
day22 = time.mktime(time.strptime(day2, "%Y %m %d %H:%M:%S"))# 將格式字符串轉換為時間戳 並賦值給day22
day=day22-day11 #將時間差賦值給day
print(day)#輸出day
if day==3: #如果時間間隔為3秒
set_event() # 創建事件
print("事件1觸發成功")
break #跳出循環
else:
print("事件1觸發失敗")
def kaoshi():
while True:
time.sleep(1) #延時1秒
if event.is_set():#如果設置了事件
print("考試結束")
break #跳出循環
else:
print("正在考試中")
def set_event():
while True:
event.set()#設置事件
time.sleep(1) #延遲1秒
if event.is_set(): # 如果間隔事件為3秒
print("事件2觸發成功")
break #跳出循環
else:
print("事件2觸發失敗")
# event.clear() #把所設置的事件清除
# time.sleep(3)
# 用類來創建多線程
#創建類 繼承自threading.Thread
class MyThread(threading.Thread):
def __init__(self): #初始化 並傳遞參數
super(MyThread, self).__init__() #繼承自父類的初始化
# 啟動線程的函數 函數名必須是run
def run(self):
shijian() #進入shijian()函數
# 用類來創建多線程
#創建類 繼承自threading.Thread
class mythread(threading.Thread):
def __init__(self): #初始化 並傳遞參數
super(mythread, self).__init__() #繼承自父類的初始化
# 啟動線程的函數 函數名必須是run
def run(self):
kaoshi() #進入kaoshi()函數
#程序主入口
if __name__ == '__main__':
event = threading.Event() # 創建事件
# semaphore = threading.Semaphore(2) # 將信號量實例化 括號裡填寫同時執行線程的個數
#調用類多線程
r1=MyThread() #實例化所創建的類
r1.start() # 啟動多線程
r2=mythread() #實例化所創建的類
r2.start() # 啟動多線程