python3之線程(一)


線程的概念

現在的操作系統幾乎都支持運行多個任務,而在操作系統內部,一個任務往往代表的執行的某一個程序,也就是運行中的程序,運行的程序是一個動態的概念,也就是所說的進程,而在進程內部,往往有許多順序執行流,這些順序執行流就是線程。

線程的創建

Python提供了 _thread 和 threading 兩個模塊來支持多線程,其中 _thread 提供低級別的、原始的線程支持,以及一個簡單的鎖,正如它的名字所暗示的,一般編程不建議使用 thread 模塊;而 threading 模塊則提供了功能豐富的多線程支持。

Python 主要通過兩種方式來創建線程:

  1. 使用 threading 模塊的 Thread 類的構造器創建線程。
  2. 繼承 threading 模塊的 Thread 類創建線程類。

使用Thread類創建線程

Thread類的構造或者說初始化的__init__方法如下:

__init__(self, group=None, target=None, name=None, args=(), kwargs=None, *,daemon=None)

上面的構造器涉及如下幾個參數:

  • group:指定該線程所屬的線程組。目前該參數還未實現,因此它只能設為 None。
  • target:指定該線程要調度的目標方法。
  • args:指定一個元組,以位置參數的形式為 target 指定的函數傳入參數。元組的第一個元素傳給 target 函數的第一個參數,元組的第二個元素傳給 target 函數的第二個參數……依此類推。
  • kwargs:指定一個字典,以關鍵字參數的形式為 target 指定的函數傳入參數。
  • daemon:指定所構建的線程是否為后代線程。

通過 Thread 類的構造器創建井啟動多線程的步驟如下:

  1. 調用 Thread 類的構造器創建線程對象。在創建線程對象時,target 參數指定的函數將作為線程執行體。
  2. 調用線程對象的 start() 方法啟動該線程。

示例:

import  threading

#定義一個函數,該函數返回當前執行的進程名字
def fc(value):
    for i in range(value):
    # 調用threading模塊current_thread()函數獲取當前線程
    # 線程對象的getName()方法獲取當前線程的名字
print(threading.current_thread().getName()
+ "--->" + str(i)) #定義線程執行體 for i in range(10): print(threading.current_thread().getName() + '--->' + str(i)) if i == 5: #當if條件成立時,執行子線程 sd = threading.Thread(target=fc, args=(10, )) sd.start() print("主線程執行完畢")

程序可以通過 setName(name) 方法為線程設置名字,也可以通過 geName() 方法返回指定線程的名字,這兩個方法可通過 name 屬性來代替。在默認情況下,主線程的名字為 MainThread,用戶啟動的多個線程的名字依次為 Thread-1、Thread-2、Thread-3、...、Thread-n 等。

繼承 threading 模塊的 Thread 類創建線程類

通過繼承 Thread 類來創建並啟動線程的步驟如下:

  1. 定義 Thread 類的子類,並重寫該類的 run() 方法。run() 方法的方法體就代表了線程需要完成的任務,因此把 run() 方法稱為線程執行體。
  2. 創建 Thread 子類的實例,即創建線程對象。
  3. 調用線程對象的 start() 方法來啟動線程。

示例:

import  threading

class DefineThread(threading.Thread):

    def __init__(self):
        #調用父類的初始化方法。
        threading.Thread.__init__(self) #或者
        #super().__init__()
        self.i = 0
      #重新定義run方法,也就是線程的執行體
    def run(self):
        while self.i < 5:
            print(threading.current_thread().name  + "--->" + str(self.i))
            self.i += 1

#主程序
for i in range(10):
    print(threading.current_thread().name + '--->' + str(i))
    if i == 5:
        dt = DefineThread()
        dt.start()

print("主線程執行完畢")

 方法

join方法

Thread 提供了讓一個線程等待另一個線程完成的 join() 方法。當調用程序調用另外一個線程的join()方法時,需要等待另外一個線程執行完成才繼續執行當前的程序。也就是說當前的執行程序會被阻塞。

示例:

import  threading

def ft(value):
    for i in range(value):
        print(threading.current_thread().name + "--->" +str(i))


#主程序
for i in range(5):
    if i == 2:
        dt = threading.Thread(target=ft, args=(5, ), name = "被其他線程調用join()方法的線程")
        dt.start()
        dt.join()
    print(threading.current_thread().name + "--->" + str(i))

print("主線程執行完畢")

輸出如下:

MainThread--->0
MainThread--->1
被其他線程調用join()方法的線程--->0
被其他線程調用join()方法的線程--->1
被其他線程調用join()方法的線程--->2
被其他線程調用join()方法的線程--->3
被其他線程調用join()方法的線程--->4
MainThread--->2
MainThread--->3
MainThread--->4
主線程執行完畢

可以看出,當子線程在執行的時候,主線程是被阻塞的。

daemon(守護或者后台進程)

daemon屬性用來設置線程運行在后台,默認是運行在前台。當所有的前台進程都死掉時,后台進程也結束,不管后台進程是否運行完畢。

示例

import  threading

def ft(value):
    for i in range(value):
        print(threading.current_thread().name + "--->" +str(i))

t  = threading.Thread(target=ft, args=(100, ), name = "后台線程", daemon=True)
t.start()
#如果沒有在創建線程對象指定daemon=True,那么也可以通過下面的屬性設置
#t  = threading.Thread(target=ft, args=(100, ), name = "后台線程")
#t.daemon = True

#主程序
for i in range(5):
    print(threading.current_thread().name + "--->" + str(i))


print("主線程執行完畢")

上述程序在執行的時候,正常應該是t這個線程對象執行到99的時候才推出,但是,在主程序內部的線程執行完畢(也就是前台進程完畢),那么后台進程也會隨之退出。

從上面的程序可以看出,主線程默認是前台線程,t線程默認也是前台線程。但並不是所有的線程默認都是前台線程,有些線程默認就是后台線程。前台線程創建的子線程默認是前台線程,后台線程創建的子線程默認是后台線程。

可見,創建后台線程有兩種方式:

  1. 主動將線程的 daemon 屬性設置為 True。
  2. 后台線程啟動的線程默認是后台線程。


注意,當前台線程死亡后,Python 解釋器會通知后台線程死亡,但是從它接收指令到做出響應需要一定的時間。如果要將某個線程設置為后台線程,則必須在該線程啟動之前進行設置。也就是說,將 daemon 屬性設為 True,必須在 start() 方法調用之前進行,否則會引發 RuntimeError 異常。


免責聲明!

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



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