信號量(Semaphore)
之前講的線程鎖(互斥鎖) 同時只允許一個線程更改數據,而Semaphore是同時允許一定數量的線程更改數據 ,比如廁所有3個坑,那最多只允許3個人上廁所,后面的人只能等里面有人出來了才能再進去。
1、信號量
- 是一個變量,控制着對公共資源或者臨界區的訪問。信號量維護着一個計數器,指定可同時訪問資源或者進入臨界區的線程數。
- 每次有一個線程獲得信號量時,計數器-1。若計數器為0,其他線程就停止訪問信號量,直到另一個線程釋放信號量
說白了就是在同一時間,可以只允許設定的數量的線程去執行
import threading
import time
def run(n):
semaphore.acquire() # 加信號量鎖
time.sleep(5)
print("run the thread: %s\n" % n)
semaphore.release() # 釋放信號量鎖
if __name__ == '__main__':
semaphore = threading.BoundedSemaphore(5) # 最多允許5個線程同時運行(Bounded:綁定,Semaphore:信號量)
for i in range(20):
t = threading.Thread(target=run, args=(i,))
t.start()
while threading.active_count() != 1:
pass
else:
print('----all threads done---')
上面程序的執行,會讓人感覺是:分了4組,前5個同時完成,然后又5個同時進去。但是實際的效果是:這5個里面如果有3個完成,就會立刻再放3個進去。不會等5個都完成,每出來1個就放進去1個,出來幾個放進去幾個
2、使用場景和總結
- 連接池,線程池,MySQL的有連接池,同一時間有多少個並發,就能連多少個連接。
- 我們為了保證我的socket_server,因為python不會默認現在你啟動多少個線程,但是你啟動的線程越多,就會把系統拉的越慢,就會把程序拉的越慢。這里就可以搞一個我同一時間放100線程個進來,就是用semaphore
- python3.x 雖然不加鎖也是正確的,但是最好還是把鎖加上
event(紅綠燈例子)
日常生活中經常遇到紅綠燈,我們就很好理解紅綠燈的例子,就是紅燈停,綠燈行。
現在生成一個線程,這個線程我讓它扮演紅綠燈,它每過一段時間就變成綠燈,又過一段時間變成紅燈,又變成黃燈。然后再生成3-5個線程作為車。車看見紅燈,它就停下來等着,如果說是綠燈,車子就走。所以就涉及到紅燈這個線程,紅綠燈的這個線程就跟車線程之前產生了依賴了。就是紅綠燈這個線程必須在綠燈的時候才能走,在紅燈的時候就立刻停下來。所以互相之前,一個線程會根據另外一個線程的狀態產生一些變化。類似這種場景的實現,就是:event,即事件。
1、事件的基礎內置函數
event = threading.Event() # 設置一個事件的全局變量 event.is_set()# 判斷是否已經設置標志位。 event.wait() # 沒有設置標志位的時候會阻塞,一遇到標志位就不會阻塞 #判斷是否已經設置標志位。 event.set() # 設置標志位 ,標志位設置了,代表着綠燈,直接通行。 event.clear() # 清除標志位,標志位被清空,代表紅燈,wait等待變綠燈。
2、紅綠燈例子
import threading
import time
event = threading.Event() # 生成線程事件實例
def lighter():
"""
模擬紅綠燈
:return:
"""
count = 0
event.set() # 先設置標志位,代表綠燈
while True:
if count > 5 and count < 10: # 改成紅燈
event.clear() # 清除標志位,變成紅燈
print("\033[41m red light is on ....\033[0m")
elif count > 10:
event.set() # 創建標志位,變成綠燈
count = 0
else:
print("\033[42m green light is on ....\033[0m")
time.sleep(1)
count += 1
def car(name):
"""
模擬車子
:param name:
:return:
"""
while True:
if event.is_set(): # 有標志位,代表是綠燈
print("{0} running ....".format(name))
time.sleep(1)
else: # 如果不是綠燈就代表紅燈
print("{0} sees red light ,waiting ....".format(name))
event.wait() # 阻塞
print("\033[32m green light is on , start going ...\033[0m")
light = threading.Thread(target=lighter,) # 啟動代表紅綠燈的線程
light.start()
car1 = threading.Thread(target=car, args=("car1",)) # 啟動代表車的線程
car1.start()
3、員工刷卡例子
員工進公司門要刷卡, 我們這里設置一個線程是“門”, 再設置幾個線程為“員工”,員工看到門沒打開,就刷卡,刷完卡,門開了,員工就可以通過。
import threading
import time
import random
def door():
door_open_time_counter = 0
while True:
if door_swiping_event.is_set():
print("\033[32;1m door opening....\033[0m")
door_open_time_counter +=1
else:
print("\033[31;1m door closed...., swipe to open.\033[0m")
door_open_time_counter = 0 # 清空計時器
door_swiping_event.wait()
if door_open_time_counter > 3: # 門開了已經3s了,該關了
door_swiping_event.clear()
time.sleep(0.5)
def staff(n):
print("staff [%s] is coming..." % n )
while True:
if door_swiping_event.is_set():
print("\033[34;1m door is opened, passing.....\033[0m")
break
else:
print("staff [%s] sees door got closed, swiping the card....." % n)
print(door_swiping_event.set())
door_swiping_event.set()
print("after set ", door_swiping_event.set())
time.sleep(0.5)
if __name__ == "__main__":
door_swiping_event = threading.Event() # 設置事件
door_thread = threading.Thread(target=door)
door_thread.start()
for i in range(20):
p = threading.Thread(target=staff, args=(i,))
time.sleep(random.randrange(3))
p.start()
