知識點:進程是分配資源的單位,線程是運算調度的單位。進程相當於資源,線程相當於控制流。
當一個進程建立時,就會有一個主線程。
進程當中的資源,如果只有一個線程在消耗,那無疑會余下空閑資源被浪費,此時就需要多線程去協同調度進程內的資源。
知識點:守護線程會隨着主線程結束而結束,守護進程會隨着主進程結束而結束。
例如 QQ關閉時,聊天窗口一並關閉。QQ是進程,聊天窗口也是進程,不過是主進程與子進程(守護進程)的關系。
setDaemon():設置守護線程
threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None) 參數列表中target是指定執行的任務,args是傳參
Thread 的生命周期
-
- 創建對象時,代表 Thread 內部被初始化。
- 調用 start() 方法后,thread 會開始運行。
- 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}] ")
執行結果: