多線程簡介
線程(Thread)也稱輕量級進程,時操作系統能夠進行運算調度的最小單位,它被包涵在進程之中,時進程中的實際運作單位。線程自身不擁有資源,只擁有一些在運行中必不可少的資源,但他可與同屬一個進程的其他線程共享進程所擁有的全部資源。一個線程可以創建和撤銷另一個線程,同一進程中的多個線程之間可以並發執行。
線程有就緒/阻塞/運行三種基本狀態:
1/ 就緒狀態是指線程具備運行的所有條件,邏輯上可以運行,在等待處理機
2 / 運行狀態是指線程占有處理機正在運行。
3/ 阻塞狀態是指線程在等待一個事件(如某個信號量),邏輯上不可執行
結論:在python中,對於計算密集型任務,多進程占優勢;對於I/O密集型任務,多線程占優勢
python多線程其它介紹
1/1 多線程threading模塊
方法一 創建threading.Thread類的實例,調用其start()方法
方法二 繼承Thread類,在子類中重寫run()和init()方法
1/2 多線程同步Lock(互訴鎖)
#如果多個線程共同對某個數據修改,則可能出現不可預料的結果,這個時候就需要使用互訴鎖來進行同步
#調用鎖
lock = threading.Lock()
#獲取鎖,用戶線程同步
lock.acquire()
1/3 多線程同步Semaphore(信號量)
互訴鎖是只允許一個線程訪問共享數據,而信號量是同時允許一定數量的線程訪問共享數據
semaphore = threading.BoundedSemaphore()
練習:
import threading
import time
semaphore = threading.BoundedSemaphore(5)
def yewubanli(name):
semaphore.acquire()
time.sleep(3)
print(f"{time.strftime('%Y-%m-%d %H:%M:%S')} {name} 正在辦理業務")
semaphore.release()
thread_list = []
for i in range(12):
t = threading.Thread(target=yewubanli, args=(i,))
thread_list.append(t)
for thread in thread_list:
thread.start()
for thread in thread_list:
thread.join()
1/4 多線程同步Condition
條件對象Condition能讓一個線程A停下來,等待其它線程B,線程B滿足了某個條件后通知(notify)線程A繼續運行。線程首先獲取一個條件變量鎖,如果條件不足,則該線程等待(wait)並釋放條件變量鎖;如果條件滿足,就繼續執行線程,執行完成后可以通知(notify)其它狀態為wait的線程執行。其它處於wait狀態的線程接到通知后會重新判斷條件以確定是否繼續執行。
cond = threading.Condition()
1/5 多線程同步Event
事件用於線程之間的通信。一個線程發出一個信號,其它一個或多個線程等待,調用Event對象的wait方法,線程則會阻塞等待,直到別的線程set之后才會被喚醒
cond = threading.Event()
1/6 線程優先級隊列(queue)
Python的queue模塊中提供了同步的/線程安全的隊列類,包括先進先出隊列Queue/后進后出隊列LifoQueue和優先級隊列PriorityQueue。這些隊列都實現了鎖原語,可以直接使用來實現線程之間的同步。
練習:
import threading, time
import queue
q = queue.Queue(maxsize=5)
def ProducerA():
count = 1
while True:
q.put(f"冷飲 {count}")
print(f"{time.strftime('%H:%M:%S')} A 放入:[冷飲 {count}]")
count += 1
time.sleep(1)
def ConsumerB():
while True:
print(f"{time.strftime('%H:%M:%S')} B 取出 [{q.get()}]")
time.sleep(5)
p = threading.Thread(target=ProducerA)
c = threading.Thread(target=ConsumerB)
c.start()
p.start()
1/7 多線程之線程池pool
在面向對象編程中,創建和銷毀對象是很費時間的,因為創建一個對象要獲取內存資源或其它更多資源。虛擬機也將視圖跟蹤每一個對象,以便能夠在對象銷毀后進行垃圾回收。同樣的道理,多任務情況下每次都會生成一個新線程,執行任務后資源再被回收就顯得非常低效,因此線程池就是解決這個問題的方法。類似的還有連接池/進程池等。
將任務添加到線程池中,線程池會自動指定一個空閑的線程取執行任務,當超過線程池的最大線程數時,任務需要等待有新的空閑線程后才會被執行。
我們可以使用threading模塊及queue模塊定制線程池,也可以使用multiprocessing。from multiprocessing import Pool這樣導入的Pool表示的時進程池,from multiprocessing.dummy import Pool這樣導入的Pool表示的時線程池
練習:
from multiprocessing.dummy import Pool as ThreadPool
import time
def fun(n):
time.sleep(2)
start = time.time()
for i in range(5):
fun(i)
print("單線程順序執行耗時:", time.time() - start)
start2 = time.time()
pool = ThreadPool(processes=5)
results2 = pool.map(fun, range(5))
pool.close()
pool.join()
print("線程池(5)並發執行耗時:",time.time() - start2)
總結:
Python多線程適合用再I/O密集型任務中。I/O密集型任務較小時間用再CPU計算上,較多時間用再I/O上,如文件讀寫/web請求/數據庫請求等;而對於計算密集型任務,應該使用多進程