Python 多執行緒實踐總結
Python 多執行緒實踐總結
0 背景
多執行緒類似於同時執行多個不同程式,多執行緒執行有如下優點:
使用執行緒可以把佔據長時間的程式中的任務放到後臺去處理。
程式的執行速度可能加快
在一些等待的任務實現上如使用者輸入、檔案讀寫和網路收發資料等,可以同時進行
程序與執行緒:
程序是資源分配的最小單位,一個程式至少有一個程序。
執行緒是程式執行的最小單位,一個程序至少有一個執行緒。
程序都有自己獨立的地址空間,記憶體,資料棧等,所以程序佔用資源多。由於程序的資源獨立,所以通訊不方便,只能使用程序間通訊(IPC)。
執行緒共享程序中的資料,他們使用相同的地址空間,使用執行緒建立快捷,建立開銷比程序小。同一程序下的執行緒共享全域性變數、靜態變數等資料,所以執行緒通訊非常方便,但會存在資料同步與互斥的問題,如何處理好同步與互斥是編寫多執行緒程式的難點。
一個程序中可以存在多個執行緒,在單核CPU中每個程序中同時刻只能執行一個執行緒,只有在多核CPU中才能存線上程併發的情況。
當執行緒需要執行但沒有執行空間時,會對執行緒的優先順序進行判斷,高優先順序先執行,低優先順序程序讓行。
1 關於併發
併發相關的python庫
threading:基於執行緒的並行
multiprocessing:基於程序的並行
concurrent:併發包
concurrent.futures:啟動並行任務
subprocess:子程序管理
sched:事件排程
queue:同步佇列
select:等待I / O完成
dummy_threading:threading模組的替代(當_thread不可用時)
_thread:底層的執行緒API(threading基於其上)
_dummy_thread:_thread模組的替代(當_thread不可用時)
1.1 什麼是執行緒?
程序是作業系統分配程式執行資源的單位,而執行緒是程序的一個實體,是CPU排程和分配的單位。一個程序肯定有一個主執行緒,我們可以在一個程序裡建立多個執行緒來實現多工。
1.2 建立threading.Thread物件
import threading
p1 = threading.Thread(target=[函式名],args=([要傳入函式的引數]))
p1.start() # 啟動p1執行緒
1.3 利用互斥鎖解決執行緒安全
當多個執行緒幾乎同時修改某一個共享資料的時候,需要進行同步控制
執行緒同步能夠保證多個執行緒安全訪問競爭資源,最簡單的同步機制是引入互斥鎖。
互斥鎖為資源引入一個狀態:鎖定/非鎖定
某個執行緒要更改共享資料時,先將其鎖定,此時資源的狀態為“鎖定”,其他執行緒不能更改;直到該執行緒釋放資源,將資源的狀態變成“非鎖定”,其他的執行緒才能再次鎖定該資源。互斥鎖保證了每次只有一個執行緒進行寫入操作,從而保證了多執行緒情況下資料的正確性。
互斥鎖有三個常用步驟:
lock = threading.Lock() # 取得鎖
lock.acquire() # 上鎖
lock.release() # 解鎖
import threading
import time
num = 0
lock = threading.Lock() # 取得鎖
def work1(loop):
global num
for i in range(loop):
# 等價於 num += 1
lock.acquire() # 上鎖
temp = num
num = temp + 1
lock.release() # 解鎖
print(num)
def work2(loop):
global num
for i in range(loop):
# 等價於 num += 1
lock.acquire() # 上鎖
temp = num
num = temp + 1
lock.release() # 解鎖
print(num)
if __name__ == '__main__':
t1 = threading.Thread(target=work1,args=(1000000,))
t2 = threading.Thread(target=work2, args=(1000000,))
t1.start()
t2.start()
while len(threading.enumerate()) != 1:
time.sleep(1)
print(num)
1.4 使用Queue模組來共享資源
from queue import Queue
from threading import Thread
import time
que = Queue()
# 如果maxsize設定為小於0或者不設定,佇列為無限長
#que = Queue(maxsize=2)
def task(value):
global que
que.put(value)
# que.put(value)
def task2():
global que
res = que.get()
print(res)
que.task_done()
t1 = Thread(target=task, args=(10,))
t2 = Thread(target=task2)
t1.start()
t2.start()
t1.join()
t2.join()
que.join()
2 參考Demo
這裡整理了一個使用que與threading 實現併發的Demo
def main():
start_url = Queue.Queue() # 存放url的佇列
result_queue = Queue.Queue() # 結果集佇列
for i in range(1, 3):
page_url = '_%s.shtml' % i
start_url.put(page_url) # 將值插入佇列中
# 構建執行緒
thread_list = []
for n in range(4): # 建立4 個執行緒
t_t = threading.Thread(target=get_news_url, args=(start_url, result_queue)) # 建立執行緒,呼叫get_news_url方法,args傳入引數
thread_list.append(t_t)
for t in thread_list:
t.start()
start_url.join() # 就是當所有的url全部獲取完,放入到結果集裡才開始存入資料庫,防止出現 插入資料庫報錯的情況
while result_queue.qsize(): # 返回佇列的大小
save_news_mysql(result_queue.get()) # 將結果存入資料庫中
3 總結
多執行緒可以實現併發 增強程式的處理能力