Python:線程

來源:互聯網
上載者:User

標籤:join   返回   blog   命名   簡單   調用   pos   balance   操作   

Python的線程是真正的Posix Thread,而不是類比出來的線程。

Python的標準庫提供了兩個模組:_threadthreading_thread是低級模組,threading是進階模組,對_thread進行了封裝。絕大多數情況下,我們只需要使用threading這個進階模組。

啟動一個線程就是把一個函數傳入並建立Thread執行個體,然後調用start()開始執行:

import time, threading# 新線程執行的代碼:def loop():    print(‘thread %s is running...‘ % threading.current_thread().name)    n = 0    while n < 5:        n = n + 1        print(‘thread %s >>> %s‘ % (threading.current_thread().name, n))        time.sleep(1)    print(‘thread %s ended.‘ % threading.current_thread().name)print(‘thread %s is running...‘ % threading.current_thread().name)t = threading.Thread(target=loop, name=‘LoopThread‘)t.start()t.join()print(‘thread %s ended.‘ % threading.current_thread().name)

執行結果

thread MainThread is running...thread LoopThread is running...thread LoopThread >>> 1thread LoopThread >>> 2thread LoopThread >>> 3thread LoopThread >>> 4thread LoopThread >>> 5thread LoopThread ended.thread MainThread ended.

分析

由於任何進程預設就會啟動一個線程,我們把該線程稱為主線程,主線程又可以啟動新的線程,Python的threading模組有個current_thread()函數,它永遠返回當前線程的執行個體。主線程執行個體的名字叫MainThread,子線程的名字在建立時指定,我們用LoopThread命名子線程。名字僅僅在列印時用來顯示,完全沒有其他意義,如果不起名字Python就自動給線程命名為Thread-1Thread-2……

Lock

多線程和多進程最大的不同在於,多進程中,同一個變數,各自有一份拷貝存在於每個進程中,互不影響,而多線程中,所有變數都由所有線程共用,所以,任何一個變數都可以被任何一個線程修改,因此,線程之間共用資料最大的危險在於多個線程同時改一個變數,把內容給改亂了。

import time, threading# 假定這是你的銀行存款:balance = 0lock = threading.Lock()def change_it(n):    # 先存後取,結果應該為0:    global balance    balance = balance + n    balance = balance - ndef run_thread(n):    for i in range(100000):        lock.acquire() # 先要擷取鎖:        try: # 放心地改吧:            change_it(n)        finally: # 改完了一定要釋放鎖:            lock.release()t1 = threading.Thread(target=run_thread, args=(5,))t2 = threading.Thread(target=run_thread, args=(8,))t1.start()t2.start()t1.join()t2.join()print(balance)

分析

兩個線程同時一存一取,就可能導致餘額不對,你肯定不希望你的銀行存款莫名其妙地變成了負數,所以,我們必須確保一個線程在修改balance的時候,別的線程一定不能改。

如果我們要確保balance計算正確,就要給change_it()上一把鎖,當某個線程開始執行change_it()時,我們說,該線程因為獲得了鎖,因此其他線程不能同時執行change_it(),只能等待,直到鎖被釋放後,獲得該鎖以後才能改。由於鎖只有一個,無論多少線程,同一時刻最多隻有一個線程持有該鎖,所以,不會造成修改的衝突。建立一個鎖就是通過threading.Lock()來實現:

多線程閱讀: 107502

多任務可以由多進程完成,也可以由一個進程內的多線程完成。

我們前面提到了進程是由若干線程組成的,一個進程至少有一個線程。

由於線程是作業系統直接支援的執行單元,因此,進階語言通常都內建多線程的支援,Python也不例外,並且,Python的線程是真正的Posix Thread,而不是類比出來的線程。

Python的標準庫提供了兩個模組:_threadthreading_thread是低級模組,threading是進階模組,對_thread進行了封裝。絕大多數情況下,我們只需要使用threading這個進階模組。

啟動一個線程就是把一個函數傳入並建立Thread執行個體,然後調用start()開始執行:

import time, threading# 新線程執行的代碼:def loop():    print(‘thread %s is running...‘ % threading.current_thread().name)    n = 0    while n < 5:        n = n + 1        print(‘thread %s >>> %s‘ % (threading.current_thread().name, n))        time.sleep(1)    print(‘thread %s ended.‘ % threading.current_thread().name)print(‘thread %s is running...‘ % threading.current_thread().name)t = threading.Thread(target=loop, name=‘LoopThread‘)t.start()t.join()print(‘thread %s ended.‘ % threading.current_thread().name)

執行結果如下:

thread MainThread is running...thread LoopThread is running...thread LoopThread >>> 1thread LoopThread >>> 2thread LoopThread >>> 3thread LoopThread >>> 4thread LoopThread >>> 5thread LoopThread ended.thread MainThread ended.

由於任何進程預設就會啟動一個線程,我們把該線程稱為主線程,主線程又可以啟動新的線程,Python的threading模組有個current_thread()函數,它永遠返回當前線程的執行個體。主線程執行個體的名字叫MainThread,子線程的名字在建立時指定,我們用LoopThread命名子線程。名字僅僅在列印時用來顯示,完全沒有其他意義,如果不起名字Python就自動給線程命名為Thread-1Thread-2……

Lock

多線程和多進程最大的不同在於,多進程中,同一個變數,各自有一份拷貝存在於每個進程中,互不影響,而多線程中,所有變數都由所有線程共用,所以,任何一個變數都可以被任何一個線程修改,因此,線程之間共用資料最大的危險在於多個線程同時改一個變數,把內容給改亂了。

來看看多個線程同時操作一個變數怎麼把內容給改亂了:

import time, threading# 假定這是你的銀行存款:balance = 0def change_it(n):    # 先存後取,結果應該為0:    global balance    balance = balance + n    balance = balance - ndef run_thread(n):    for i in range(100000):        change_it(n)t1 = threading.Thread(target=run_thread, args=(5,))t2 = threading.Thread(target=run_thread, args=(8,))t1.start()t2.start()t1.join()t2.join()print(balance)

我們定義了一個共用變數balance,初始值為0,並且啟動兩個線程,先存後取,理論上結果應該為0,但是,由於線程的調度是由作業系統決定的,當t1、t2交替執行時,只要迴圈次數足夠多,balance的結果就不一定是0了。

原因是因為進階語言的一條語句在CPU執行時是若干條語句,即使一個簡單的計算:

balance = balance + n

也分兩步:

  1. 計算balance + n,存入臨時變數中;
  2. 將臨時變數的值賦給balance

也就是可以看成:

x = balance + nbalance = x

由於x是局部變數,兩個線程各自都有自己的x,當代碼正常執行時:

初始值 balance = 0t1: x1 = balance + 5 # x1 = 0 + 5 = 5t1: balance = x1     # balance = 5t1: x1 = balance - 5 # x1 = 5 - 5 = 0t1: balance = x1     # balance = 0t2: x2 = balance + 8 # x2 = 0 + 8 = 8t2: balance = x2     # balance = 8t2: x2 = balance - 8 # x2 = 8 - 8 = 0t2: balance = x2     # balance = 0結果 balance = 0

但是t1和t2是交替啟動並執行,如果作業系統以下面的順序執行t1、t2:

初始值 balance = 0t1: x1 = balance + 5  # x1 = 0 + 5 = 5t2: x2 = balance + 8  # x2 = 0 + 8 = 8t2: balance = x2      # balance = 8t1: balance = x1      # balance = 5t1: x1 = balance - 5  # x1 = 5 - 5 = 0t1: balance = x1      # balance = 0t2: x2 = balance - 8  # x2 = 0 - 8 = -8t2: balance = x2   # balance = -8結果 balance = -8

究其原因,是因為修改balance需要多條語句,而執行這幾條語句時,線程可能中斷,從而導致多個線程把同一個對象的內容改亂了。

兩個線程同時一存一取,就可能導致餘額不對,你肯定不希望你的銀行存款莫名其妙地變成了負數,所以,我們必須確保一個線程在修改balance的時候,別的線程一定不能改。

如果我們要確保balance計算正確,就要給change_it()上一把鎖,當某個線程開始執行change_it()時,我們說,該線程因為獲得了鎖,因此其他線程不能同時執行change_it(),只能等待,直到鎖被釋放後,獲得該鎖以後才能改。由於鎖只有一個,無論多少線程,同一時刻最多隻有一個線程持有該鎖,所以,不會造成修改的衝突。建立一個鎖就是通過threading.Lock()來實現:

balance = 0lock = threading.Lock()def run_thread(n):    for i in range(100000):        # 先要擷取鎖:        lock.acquire()        try:            # 放心地改吧:            change_it(n)        finally:            # 改完了一定要釋放鎖:            lock.release()

當多個線程同時執行lock.acquire()時,只有一個線程能成功地擷取鎖,然後繼續執行代碼,其他線程就繼續等待直到獲得鎖為止。

獲得鎖的線程用完後一定要釋放鎖,否則那些苦苦等待鎖的線程將永遠等待下去,成為死線程。所以我們用try...finally來確保鎖一定會被釋放。

鎖的好處就是確保了某段關鍵代碼只能由一個線程從頭到尾完整地執行,壞處當然也很多,首先是阻止了多線程並發執行,包含鎖的某段代碼實際上只能以單線程模式執行,效率就大大地下降了。其次,由於可以存在多個鎖,不同的線程持有不同的鎖,並試圖擷取對方持有的鎖時,可能會造成死結,導致多個線程全部掛起,既不能執行,也無法結束,只能靠作業系統強制終止。

 

多核CPU

Python:線程

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.