python3 多執行緒

Bgods發表於2019-10-18

原文:http://bgods.cn/blog/post/15/

一、執行緒模組

Python3 透過兩個標準庫 _thread 和 threading 提供對執行緒的支援。

_thread 提供了低階別的、原始的執行緒以及一個簡單的鎖,它相比於 threading 模組的功能還是比較有限的。

threading 模組除了包含 _thread 模組中的所有方法外,還提供的其他方法:

  • threading.currentThread(): 返回當前的執行緒變數。
  • threading.enumerate(): 返回一個包含正在執行的執行緒的list。正在執行指執行緒啟動後、結束前,不包括啟動前和終止後的執行緒。
  • threading.activeCount(): 返回正在執行的執行緒數量,與len(threading.enumerate())有相同的結果。

除了使用方法外,執行緒模組同樣提供了Thread類來處理執行緒,Thread類提供了以下方法:

  • run(): 用以表示執行緒活動的方法。
  • start():啟動執行緒活動。
  • join([time]): 等待至執行緒中止。這阻塞呼叫執行緒直至執行緒的join() 方法被呼叫中止-正常退出或者丟擲未處理的異常-或者是可選的超時發生。
  • isAlive(): 返回執行緒是否活動的。
  • getName(): 返回執行緒名。
  • setName(): 設定執行緒名。

二、執行緒同步

如果多個執行緒共同對某個資料修改,則可能出現不可預料的結果,為了保證資料的正確性,需要對多個執行緒進行同步。 使用 Threading 物件的 Lock 和 Rlock 可以實現簡單的執行緒同步,這兩個物件都有 acquire 方法和 release 方法,對於那些需要每次只允許一個執行緒操作的資料,可以將其操作放到 acquire 和 release 方法之間。

利用執行緒同步,可以使部分程式碼只能同時被一個執行緒操作,當這部分程式碼被一個執行緒使用時,其他執行緒需要等待執行緒該執行緒處理完之後,才能輪到下一個執行緒呼叫

  • 例項

利用多執行緒,每個執行緒獲取全域性變數num,然後對num進行1000000次的+1操作,如果沒有利用執行緒同步,程式碼如下:

import threading
num = 0  # 全域性變數
def sum(threadName,number):
    global num
    for i in range(number):
        num += 1
    print('{} num的值是: {}'.format(threadName,num))

class myThread(threading.Thread):
    def __init__(self,number):
        threading.Thread.__init__(self)
        self.number = number
    def run(self):
        print('開始執行緒:%s' % (self.getName()))
        sum(self.getName(),self.number)
        print('結束執行緒:%s' % (self.getName()))

if __name__ == '__main__':
    threads = []  # 建立執行緒列表
    # 建立新執行緒
    thread1 = myThread(number=1000000)
    thread2 = myThread(number=1000000)
    # 新增執行緒到執行緒列表
    threads.append(thread1)
    threads.append(thread2)
    [t.start() for t in threads] # 開啟新執行緒
    [t.join() for t in threads] # 等待所有執行緒完成
    print('最後num的值是: {}'.format(num))

執行結果如下,可以看到兩個執行緒對num進行操作後,最後全域性變數num的值是1359993,和我們預想的結果(2000000)不一致。

開始執行緒:Thread-1
開始執行緒:Thread-2
Thread-1 num的值是: 1089741
結束執行緒:Thread-1
Thread-2 num的值是: 1359993
結束執行緒:Thread-2
最後num的值是: 1359993

下面我們利用執行緒同步,對上面程式碼進行改進,加入以下三行程式碼:

...
    def run(self):
        threadlock.acquire() # 獲取鎖,用於執行緒同步:當執行緒2進入時,如果以下程式碼有執行緒1在呼叫,則會暫停執行緒2,等待執行緒1解鎖後再到執行緒2進入
        print('開始執行緒:%s' % (self.getName()))
        sum(self.getName(),self.number)
        threadlock.release() # 解鎖執行緒:當執行緒1解鎖後,執行緒2才能繼續執行
        print('結束執行緒:%s' % (self.getName()))
if __name__ == '__main__':
    threadlock = threading.Lock() # 建立執行緒鎖
...

執行結果如下:

開始執行緒:Thread-1
Thread-1 num的值是: 1000000
結束執行緒:Thread-1
開始執行緒:Thread-2
Thread-2 num的值是: 2000000
結束執行緒:Thread-2
最後num的值是: 2000000

可以看到,和我們預想的結果一致。 

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章