python-多線程處理


 

Python多線程

Python 提供了多個模塊來支持多線程編程,包括 thread、threading 和 Queue 模塊等。程序是可以使用 thread 和 threading 模塊來創建與管理線程。

thread 模塊提供了基本的線程和鎖定支持;而 threading 模塊提供了更高級別、功能更全面的線程管理。

使用 Queue 模塊,用戶可以創建一個隊列數據結構,用於在多線程之間進行共享。

在python3.0中,已經將thread改名為_thread

Python 代碼的執行是由 Python 虛擬機(又名解釋器主循環)進行控制的。

對 Python 虛擬機的訪問是由全局解釋器鎖(GIL)控制的。這個鎖就是用來保證同時只能有一個線程運行的。在多線程環境中,Python 虛擬機將按照下面所述的方式執行。

1.設置 GIL。
2.切換進一個線程去運行。
3.執行下面操作之一。
  a.指定數量的字節碼指令。
  b.線程主動讓出控制權(可以調用 time.sleep(0)來完成)。
4.把線程設置回睡眠狀態(切換出線程)。
5.解鎖 GIL。
6.重復上述步驟。

當一個線程完成函數的執行時,它就會退出。另外,還可以通過調用諸如 thread.exit()之類的退出函數,或者 sys.exit()之類的退出 Python 進程的標准方法,亦或者拋出 SystemExit異常,來使線程退出。不過,你不能直接“終止”一個線程。

python多線程支持的平台:

絕大多數類 UNIX 平台(如 Linux、Solaris、Mac OS X、*BSD 等),以及Windows 平台。

Python 使用兼容 POSIX 的線程,也就是pthread,點擊跳轉百度百科

 

不使用線程的情況

我們將使用 time.sleep()函數來演示線程是如何工作的

創建兩個時間循環:一個睡眠 4 秒(loop0());另一個睡眠 2 秒(loop1()) (這里使用“loop0”和“loop1”作為函數名,暗示我們最終會有一個循環序列)。

 

import time

def loop0():
    print("start loop 0 at: %s" % time.ctime())
    time.sleep(4)
    print("loop 0 done at: %s" % time.ctime())


def loop1():
    print("start loop 1 at: %s" % time.ctime())
    time.sleep(2)
    print("loop 1 done at: %s" % time.ctime())


def main():
    print("starting at: %s" % time.ctime())
    loop0()
    loop1()
    print("all DONE at: %s" % time.ctime())


if __name__ == '__main__':
    main()
starting at: Tue Jan 23 16:03:21 2018
start loop 0 at: Tue Jan 23 16:03:21 2018
loop 0 done at: Tue Jan 23 16:03:25 2018
start loop 1 at: Tue Jan 23 16:03:25 2018
loop 1 done at: Tue Jan 23 16:03:27 2018
all DONE at: Tue Jan 23 16:03:27 2018

從輸出中我們可以看出,輸出整整花了我們7秒鍾的時間
單線程的輸出結果

 

_thread模塊-一個不建議使用的模塊

_thread模塊和鎖對象

thread 模塊的核心函數是 start_new_thread()。它的參數包括函數(對象)、函數的參數以及可選的關鍵字參數。

start_new_thread()必須包含開始的兩個參數,於是即使要執行的函數不需要參數,也需要傳遞一個空元組。

 

使用_thread模塊進行編程:

我們只需要將上面的代碼進行稍微改動一下即可

import time
import _thread


def loop0():
    print("start loop 0 at: %s" % time.ctime())
    time.sleep(4)
    print("loop 0 done at: %s" % time.ctime())


def loop1():
    print("start loop 1 at: %s" % time.ctime())
    time.sleep(2)
    print("loop 1 done at: %s" % time.ctime())


def main():
    print("starting at: %s" % time.ctime())
    _thread.start_new_thread(loop0, ())
    _thread.start_new_thread(loop1, ())
    time.sleep(5)    #暫停5秒,原因是因為我們的loop0函數暫停了4秒,如果我們小於4秒會出現無法輸出loop 0 done...
    print("all DONE at: %s" % time.ctime())


if __name__ == '__main__':
    main()
starting at: Tue Jan 23 16:14:19 2018
start loop 0 at: Tue Jan 23 16:14:19 2018
start loop 1 at: Tue Jan 23 16:14:19 2018
loop 1 done at: Tue Jan 23 16:14:21 2018
loop 0 done at: Tue Jan 23 16:14:23 2018
all DONE at: Tue Jan 23 16:14:24 2018

從輸出結果中我們可以看到loop0和loop1同時啟動了,而不會先執行完loop0在執行loop1
_thread多線程輸出結果

 

守護線程

避免使用 thread 模塊的另一個原因是該模塊不支持守護線程這個概念。當主線程退出時,所有子線程都將終止,不管它們是否仍在工作。如果你不希望發生這種行為,就要引入守護線程的概念了。threading 模塊支持守護線程,其工作方式是:守護線程一般是一個等待客戶端請求服務的服務器。如果沒有客戶端請求,守護線程就是空閑的。如果把一個線程設置為守護線程,就表示這個線程是不重要的,進程退出時不需要等待這個線程執行完成。如同在第 2 章中看到的那樣,服務器線程遠行在一個無限循環里,並且在正常情況下不會退出。如果主線程准備退出時,不需要等待某些子線程完成,就可以為這些子線程設置守護線程標記。該標記值為真時,表示該線程是不重要的,或者說該線程只是用來等待客戶端請求而不做任何其他事情。要將一個線程設置為守護線程,需要在啟動線程之前執行如下賦值語句:thread.daemon = True(調用 thread.setDaemon(True)的舊方法已經棄用了)。同樣,要檢查線程的守護狀態,也只需要檢查這個值即可(對比過去調用 thread.isDaemon()的方法)。一個新的子線程會繼承父線程的守護標記。整個 Python 程序(可以解讀為:主線程)將在所有非守護線程退出之后才退出,換句話說,就是沒有剩下存活的非守護線程時。

 

threading模塊

threading模塊的對象

對象

描述

Thread

表示一個執行線程的對象

Lock

鎖原語對象(和 thread 模塊中的鎖一樣)

RLock

可重入鎖對象,使單一線程可以(再次)獲得已持有的鎖(遞歸鎖)

Condition

條件變量對象,使得一個線程等待另一個線程滿足特定的“條件”,比如改變狀態或某個數據值

Event

條件變量的通用版本,任意數量的線程等待某個事件的發生,在該事件發生后所有線程將被激活

Semaphore

為線程間共享的有限資源提供了一個“計數器”,如果沒有可用資源時會被阻塞

BoundedSemaphore

與 Semaphore 相似,不過它不允許超過初始值

Timer

與 Thread 相似,不過它要在運行前等待一段時間

Barrier

創建一個“障礙”,必須達到指定數量的線程后才可以繼續

 

threading 模塊的 Thread 類是主要的執行對象。它有 thread 模塊中沒有的很多函數。

Thread 對象的屬性和方法

使用 Thread 類,可以有很多方法來創建線程。

 

創建Thread的實例,傳給它一個函數

代碼

import threading
import time

loops = [4, 2]


def loop(nloop, nsec):
    print("start loop %s at: %s" % (nloop, time.ctime()))
    time.sleep(nsec)
    print("loop %s done at: %s" % (nloop, time.ctime()))


def main():
    print("starting at: %s" % time.ctime())
    threads = []
    nloops = range(len(loops))
    for i in range(0, 2):
        t = threading.Thread(target=loop, args=(i, loops[i]))
        # print(type(i), i, type(loops[i]), loops[i])
        # 這個位置可能容易暈,第一次傳入args,i會=0,loops[i]會等於4,第二次循環,i=1,loops[i]=2
        # (i=0,loops[i]=4,為什么是0和4,因為i本來就=0,loops[i]里面的i=0,loops列表的第一零個本來就等於4)
        # 會傳入到loop函數中
        threads.append(t)  # 將每次循環的對象加入到列表

    for i in nloops:
        threads[i].start()  # 開啟線程

    for i in nloops:  # 等待
        threads[i].join()  # 線程完成
    print("all Done at: %s" % time.ctime())


if __name__ == '__main__':
    main()
starting at: Wed Jan 24 20:54:03 2018
start loop 0 at: Wed Jan 24 20:54:03 2018
start loop 1 at: Wed Jan 24 20:54:03 2018
loop 1 done at: Wed Jan 24 20:54:05 2018
loop 0 done at: Wed Jan 24 20:54:07 2018
all Done at: Wed Jan 24 20:54:07 2018
輸出

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM