1. 程式人生 > 其它 >Python 多執行緒實踐總結

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 總結

多執行緒可以實現併發 增強程式的處理能力