Python多線程之join()用法


 

知識點:進程是分配資源的單位,線程是運算調度的單位。進程相當於資源,線程相當於控制流。

    當一個進程建立時,就會有一個主線程。

    進程當中的資源,如果只有一個線程在消耗,那無疑會余下空閑資源被浪費,此時就需要多線程去協同調度進程內的資源。

知識點:守護線程會隨着主線程結束而結束,守護進程會隨着主進程結束而結束。

    例如 QQ關閉時,聊天窗口一並關閉。QQ是進程,聊天窗口也是進程,不過是主進程與子進程(守護進程)的關系。

  setDaemon():設置守護線程

  threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None) 參數列表中target是指定執行的任務,args是傳參

  Thread 的生命周期  

    1. 創建對象時,代表 Thread 內部被初始化。
    2. 調用 start() 方法后,thread 會開始運行。
    3. thread 代碼正常運行結束或者是遇到異常,線程會終止。

  start()方法讓線程進入runnable隊列,具體什么時候執行,由cpu決定

  join()是阻塞,將當前子線程阻塞,主線程要等待一段時間后才能繼續執行。

一、setDaemon(False) 即系統默認情況下的設置

程序代碼:

import threading
import time


def run():
    """
    : 每個線程的任務都是run
    : thread.current_thread().name:當前線程名稱
    :return: None
    """
    son_start_time = time.time()
    for i in range(1, 6):
        print(f"[線程 {threading.current_thread().name} 現在執行到了{i}]")
        time.sleep(1)
    print(f"[子線程{threading.current_thread().name}結束,耗時:{time.time() - son_start_time}]")


if __name__ == '__main__':
    start_time = time.time()
    # 用於裝線程的列表
    thread_list = []
    # 依次新建 5 個 線程
    for i in range(5):
        thread_list.append(threading.Thread(target=run))
    # 多個線程依次設置 非 守護線程,並執行
    for i in thread_list:
        i.setDaemon(False)
        i.start()
    # 主線程等待三秒
    time.sleep(3)
    print(f"[主線程{threading.current_thread().name}結束啦 ! 耗時:{time.time() - start_time}] ")

結果如圖:

 如上圖所示,主線程在執行完自己的任務后便結束,子線程繼續執行自己的任務。

當前是子線程無返回值的情況,主線程和子線程相關性並不大,那么子線程若是需要返回結果給主線程,其執行情況又是怎樣呢?

實現過程如下:

"""
    title:多線程——join試驗
    author:阿松不飛
"""

import threading
import time
from queue import Queue


def run(q):
    """
    : 每個線程的任務都是run
    : thread.current_thread().name:當前線程名稱
    :return: res
    """
    son_start_time = time.time()
    for i in range(1, 6):
        print(f"[線程 {threading.current_thread().name} 現在執行到了{i}]")
        time.sleep(1)
    # q 調用put方法把將要返回的結果放入隊列
    q.put(f"[子線程{threading.current_thread().name}結束,耗時:{time.time() - son_start_time}]")


if __name__ == '__main__':
    start_time = time.time()
    # 用於裝線程的列表
    thread_list = []
    # 新建一個隊列,用來裝取結果
    q = Queue()
    # 依次新建 5 個 線程
    for i in range(5):
        # 將隊列 q 傳入線程方法當中
        thread_list.append(threading.Thread(target=run, args=(q,)))
    # 多個線程依次設置 非 守護線程,並執行
    for i in thread_list:
        i.setDaemon(False)
        i.start()
    # 依次取出 q 隊列中的結果
    for i in range(5):
        print(q.get())
    # 主線程等待三秒
    time.sleep(3)
    print(f"[主線程{threading.current_thread().name}結束啦 ! 耗時:{time.time() - start_time}] ")

這個是隊列存取結果的方案,因為我測試了 在run方法中放入return,然后主線程通過i.start() 獲得返回值,結果總是None,便要換種獲取結果的方案。

隊列方案的運行結果如下:

那么由此可以看到,因為要獲取到子線程的返回值,所以主線程的執行過程中,會有一個等待q隊列的過程,而q隊列會在等待子線程。

 

二、setDaemon(True)

  當setDaemon為true時,被設置的線程會成為主線程的守護線程。

  主線程若是結束,則會連帶守護線程一起結束,守護線程不再執行,但是其余非守護線程還可以執行。

示例代碼如下:

import threading
import time


def run():
    """
    : 每個線程的任務都是run
    : thread.current_thread().name:當前線程名稱
    :return: res
    """
    son_start_time = time.time()
    for i in range(1, 6):
        print(f"[線程 {threading.current_thread().name} 現在執行到了{i}]")
        time.sleep(1)
    print(f"[子線程{threading.current_thread().name}結束,耗時:{time.time() - son_start_time}]")


if __name__ == '__main__':
    start_time = time.time()
    # 用於裝線程的列表
    thread_list = []
    # 依次新建 5 個 線程
    for i in range(5):
        thread_list.append(threading.Thread(target=run))
    # 多個線程依次設置 非 守護線程,並執行
    for i in thread_list:
        i.setDaemon(True)
        i.start()
    # 主線程等待三秒
    time.sleep(3)
    print(f"[主線程{threading.current_thread().name}結束啦 ! 耗時:{time.time() - start_time}] ")

其結果如圖:

  可以看到,當我設置了這些線程為守護線程之后, 主線程一旦執行完畢,守護線程隨着主線程關閉

  那么接來下測試線程中join的作用

 運行代碼:

import threading
import time


def run():
    """
    : 每個線程的任務都是run
    : thread.current_thread().name:當前線程名稱
    :return: res
    """
    son_start_time = time.time()
    for i in range(1, 6):
        print(f"[線程 {threading.current_thread().name} 現在執行到了{i}]")
        time.sleep(1)
    print(f"[子線程{threading.current_thread().name}結束,耗時:{time.time() - son_start_time}]")


if __name__ == '__main__':
    start_time = time.time()
    # 用於裝線程的列表
    thread_list = []
    # 依次新建 5 個 線程
    for i in range(5):
        thread_list.append(threading.Thread(target=run))
    # 多個線程依次設置 非 守護線程,並執行
    for i in thread_list:
        i.setDaemon(True)
        i.start()

    for i in thread_list:
        i.join()
    # 主線程等待三秒
    time.sleep(3)
    print(f"[主線程{threading.current_thread().name}結束啦 ! 耗時:{time.time() - start_time}] ")

其運行結果如下:

   

  當守護線程用上join方法的時候,join之中會有一個默認的timeout參數,在子線程執行過程中將阻塞主線程,讓主線程去等待自己,等待的時長最大為timeout的時長,最短為子線程執行結束時間(<timeout時)。一旦等待超時,主線程不再等待,繼續往下執行,當主線程執行結束,子線程也被關閉。

測試代碼:

"""
    title:多線程——join試驗
    author:阿松不飛
"""

import threading
import time


def run():
    """
    : 每個線程的任務都是run
    : thread.current_thread().name:當前線程名稱
    :return: res
    """
    son_start_time = time.time()
    for i in range(1, 6):
        print(f"[線程 {threading.current_thread().name} 現在執行到了{i}]")
        time.sleep(1)
    print(f"[子線程{threading.current_thread().name}結束,耗時:{time.time() - son_start_time}]")


if __name__ == '__main__':
    start_time = time.time()
    # 用於裝線程的列表
    thread_list = []
    # 依次新建 5 個 線程
    for i in range(5):
        thread_list.append(threading.Thread(target=run))
    # 多個線程依次設置 非 守護線程,並執行
    for i in thread_list:
        i.setDaemon(True)
        i.start()

    for i in thread_list:
        i.join(timeout=0.1)

    print(f"[主線程{threading.current_thread().name}結束啦 ! 耗時:{time.time() - start_time}] ")

 

執行結果:

 

 

 


免責聲明!

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



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