Python開發——Python 線程入門

An Intro to Threading in Python – Real Python????????

1. 什么是線程?

????????線程是一個獨立的執行流程。這意味著您的程序將有兩件事情同時發生。但對于大多數 Python 3 實現來說,不同的線程實際上并不是同時執行的:它們只是看起來是這樣。

????????人們很容易把線程想象成程序上運行著兩個(或多個)不同的處理器,每個處理器同時執行一項獨立的任務。這幾乎是對的。線程可能運行在不同的處理器上,但它們一次只能運行一個。

????????要同時運行多個任務,需要使用非標準的 Python 實現,用不同的語言編寫部分代碼,或者使用多進程,而多進程會帶來一些額外的開銷。

????????由于 CPython 實現 Python 的工作方式,線程可能無法加快所有任務的速度。這是由于與 GIL 的交互作用,基本上每次只能運行一個 Python 線程。

????????大部分時間都在等待外部事件的任務一般都適合使用線程。而那些需要大量 CPU 計算且等待外部事件的時間很少的問題,運行速度可能根本不會快。

????????這適用于用 Python 編寫并在標準 CPython 實現上運行的代碼。如果您的線程是用 C 語言編寫的,它們可以釋放 GIL 并同時運行。如果您在不同的 Python 實現上運行,請查閱文檔,了解它是如何處理線程的。

????????如果您運行的是標準 Python 實現,只用 Python 編寫,并且有 CPU 限制的問題,那么您應該選擇多處理模塊。

????????將程序架構為使用線程也能提高設計的清晰度。您將在本教程中學到的大多數示例并不一定會因為使用了線程而運行得更快。在這些示例中使用線程有助于使設計更簡潔、更易于推理。

????????所以,讓我們停止談論線程,開始使用它吧!

?2. 開始線程

????????現在您已經知道了線程是什么,讓我們來學習如何創建一個線程。Python 標準庫提供了線程,它包含了你在本文中將看到的大多數基元。本模塊中的 Thread 很好地封裝了線程,提供了一個簡潔的接口來使用它們。

????????要啟動一個單獨的線程,需要創建一個線程實例,然后告訴它 .start():

import loggingimport threadingimport timedef thread_function(name):logging.info("Thread %s: starting", name)time.sleep(2)logging.info("Thread %s: finishing", name)if __name__ == "__main__":format = "%(asctime)s: %(message)s"logging.basicConfig(format=format, level=logging.INFO,datefmt="%H:%M:%S")logging.info("Main ???: before creating thread")x = threading.Thread(target=thread_function, args=(1,))logging.info("Main ???: before running thread")x.start()logging.info("Main ???: wait for the thread to finish")x.join()logging.info("Main ???: all done")

????????如果查看一下日志語句,就會發現主要部分是創建和啟動線程:

x = threading.Thread(target=thread_function, args=(1,))x.start()

????????創建線程時,你會向它傳遞一個函數和一個包含該函數參數的列表。在本例中,你告訴線程運行 thread_function(),并將 1 作為參數傳遞給它。

????????本文將使用順序整數作為線程的名稱。threading.get_ident()可以為每個線程返回一個唯一的名稱,但這些名稱通常既不簡短也不易讀。

????????thread_function() 本身并沒有做什么。它只是簡單地記錄一些信息,并在信息之間加入 time.sleep()。

????????運行該程序(注釋掉第 20 行)時,輸出結果如下:

$ ./single_thread.pyMain ???: before creating threadMain ???: before running threadThread 1: startingMain ???: wait for the thread to finishMain ???: all doneThread 1: finishing

????????你會注意到,線程在代碼的 Main 部分結束后才結束。在下一節中,我們將回過頭來討論為什么會這樣,并談談神秘的第 20 行。

3. 守護進程線程

????????在計算機科學中,守護進程是在后臺運行的進程。

????????Python 線程對守護進程有更具體的含義。守護線程會在程序退出時立即關閉。思考這些定義的一種方法是,將守護線程視為一個在后臺運行的線程,而不必擔心關閉它。

????????如果程序運行的線程不是守護進程,那么程序會等待這些線程完成后才終止。而作為守護進程的線程則會在程序退出時被殺死。

????????讓我們再仔細看看上面程序的輸出結果。最后兩行是最有趣的部分。當你運行程序時,你會發現在 __main__ 打印出 all done 消息后,在線程結束前有一個停頓(約 2 秒)。

????????這個停頓是 Python 在等待非調用線程完成。當 Python 程序結束時,關閉過程的一部分就是清理線程例程。

????????如果您查看 Python 線程的源代碼,就會發現 threading._shutdown() 會遍歷所有正在運行的線程,并對每一個沒有設置守護進程標志的線程調用 .join()。

????????因此,程序等待退出是因為線程本身處于休眠狀態。一旦完成并打印了信息,.join() 就會返回,程序就可以退出了。

????????通常,這種行為是你想要的,但我們還有其他選擇。首先,讓我們用守護進程線程重復該程序。方法是改變線程的構造,添加 daemon=True 標志:

x = threading.Thread(target=thread_function, args=(1,), daemon=True)

????????現在運行程序,應該會看到以下輸出:

$ ./daemon_thread.pyMain ???: before creating threadMain ???: before running threadThread 1: startingMain ???: wait for the thread to finishMain ???: all done

????????這里的區別在于輸出的最后一行不見了。 thread_function() 沒有機會完成。它是一個守護線程,所以當 __main__ 的代碼結束,程序想要結束時,守護線程就被殺死了。

4. 線程的使用場景

????????線程非常適合處理以下類型的任務:

????????1. I/O 密集型任務:例如文件讀寫、網絡請求等。這類任務通常涉及大量的等待時間,線程可以在等待期間執行其他任務,從而提高效率。

????????2. 后臺任務:例如日志記錄、監控系統狀態、處理定時任務等。這類任務通常不需要立即的響應,可以在后臺異步處理。

5. 高級線程操作

5.1 使用線程池

????????在處理大量線程時,直接管理每個線程可能會變得復雜。此時,使用線程池(ThreadPoolExecutor)可以簡化管理。

from concurrent.futures import ThreadPoolExecutorimport loggingimport threadingimport timedef thread_function(name):logging.info("Thread %s: starting", name)time.sleep(2)logging.info("Thread %s: finishing", name)if __name__ == "__main__":format = "%(asctime)s: %(message)s"logging.basicConfig(format=format, level=logging.INFO,datefmt="%H:%M:%S")logging.info("Main ???: before creating thread pool")with ThreadPoolExecutor(max_workers=3) as executor:for i in range(5):executor.submit(thread_function, i)logging.info("Main ???: all done")

????????在這個例子中,ThreadPoolExecutor 創建了一個包含最多 3 個工作線程的線程池,并提交了 5 個任務。線程池會自動管理線程的生命周期和任務調度。

5.2 線程同步

????????在線程之間共享數據時,需要注意線程安全問題。Python 提供了多種同步機制,例如鎖(Lock)、信號量(Semaphore)和條件變量(Condition)。

????????鎖(Lock)

import threadinglock = threading.Lock()shared_resource = 0def thread_safe_increment():global shared_resourcewith lock:shared_resource += 1threads = []for _ in range(5):t = threading.Thread(target=thread_safe_increment)threads.append(t)t.start()for t in threads:t.join()print(shared_resource)

????????信號量(Semaphore)

????????信號量適用于需要限制同時訪問資源的線程數量的場景。

import threadingimport timesemaphore = threading.Semaphore(3)def access_resource(name):with semaphore:print(f"{name} is accessing the resource")time.sleep(2)print(f"{name} is releasing the resource")threads = []for i in range(5):t = threading.Thread(target=access_resource, args=(f"Thread-{i}",))threads.append(t)t.start()for t in threads:t.join()

5.3 線程間通信

????????Python 提供了隊列(Queue)來實現線程之間的安全通信。

import threadingimport queueimport timeq = queue.Queue()def producer():for i in range(5):print(f"Producing {i}")q.put(i)time.sleep(1)def consumer():while True:item = q.get()if item is None:breakprint(f"Consuming {item}")time.sleep(2)t1 = threading.Thread(target=producer)t2 = threading.Thread(target=consumer)t1.start()t2.start()t1.join()q.put(None)t2.join()

????????在這個例子中,生產者線程將數據放入隊列,而消費者線程從隊列中取出數據進行處理。

6. 多線程的注意事項

????????1. 避免死鎖:當多個線程互相等待對方釋放資源時,會導致程序卡死。

????????2. GIL 的限制:由于 Python 的全局解釋器鎖(GIL),多線程并不能真正并行執行 CPU 密集型任務。可以考慮使用多進程來繞過這個限制。

????????3. 資源清理:確保在程序結束時正確關閉和清理線程。

7. 結論

????????通過以上的介紹和示例,相信您已經對 Python 中的線程有了更深入的理解。線程在處理 I/O 密集型任務和后臺任務時非常有用,但在使用時需要注意線程同步和通信等問題,以避免線程安全問題。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/40261.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/40261.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/40261.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Vue3中的jsx的babel配置

如果我們希望在項目中使用jsx,那么我們需要添加對jsx的支持: jsx我們通常會通過Babel來進行轉換(React編寫的jsx就是通過babel轉換的);對于Vue來說,我們只需要在Babel中配置對應的插件即可; *…

Vue+Xterm.js+WebSocket+JSch實現Web Shell終端

一、需求 在系統中使用Web Shell連接集群的登錄節點 二、實現 前端使用Vue&#xff0c;WebSocket實現前后端通信&#xff0c;后端使用JSch ssh通訊包。 1. 前端核心代碼 <template><div class"shell-container"><div id"shell"/>&l…

C++ 實現字符串逆序

C 實現字符串逆序 思路&#xff1a; 輸入一個字符串。使用雙指針法&#xff0c;交換字符串的首尾字符&#xff0c;逐步向中間移動。輸出逆序后的字符串。 #include <iostream> #include <string>using namespace std;void reverseString(string &str) {int …

【FPGA】STA靜態時序分析

文章目錄 一.定義二.分類1. 靜態時序分析2. 靜態時序分析 三. 概念四. 時間余量1.場景2.建立時間余量3.保持時間余量 一.定義 時序分析:檢查電路是否滿足時序要求&#xff1b; 二.分類 1. 靜態時序分析 STA,遍歷所有的時序路徑&#xff0c;根據時序庫&#xff08;.lib文件&…

【Mojolicious RESTful接口全解】構建現代化Web服務的秘訣

標題&#xff1a;【Mojolicious RESTful接口全解】構建現代化Web服務的秘訣 Mojolicious是一個基于Perl的高性能、實時的Web框架&#xff0c;它以其簡潔的語法和強大的功能而聞名。Mojolicious不僅支持傳統的Web應用開發&#xff0c;還特別適合構建RESTful API。本文將詳細介紹…

新手教學系列——使用uWSGI對Flask應用提速

在構建和部署Flask應用時,性能和穩定性是兩個關鍵的因素。為了提升Flask應用的性能,我們可以借助uWSGI這個強大的工具。本文將詳細介紹為什么要使用uWSGI、uWSGI的底層原理,并提供一個實例配置,幫助你更好地理解和應用這個工具。 為什么要使用uWSGI uWSGI 是一個應用服務…

探索企業知識邊界,鴻翼ECM AI助手開啟智慧問答新時代

在信息化迅速發展的當下&#xff0c;企業積累的數字文檔數量巨大&#xff0c;這些文檔中蘊含的深層信息對業務發展至關重要。然而&#xff0c;傳統的搜索技術常常因只能進行關鍵字查詢而無法滿足對文檔深層次理解的需求。 據Gartner調查&#xff0c;高達47%的員工在尋找有效工…

Webpack: 三種Chunk產物的打包邏輯

概述 在前文 Webpack: Dependency Graph 管理模塊間依賴 中&#xff0c;我們已經詳細講解了「構建」階段如何從 Entry 開始逐步遞歸讀入、解析模塊內容&#xff0c;并最終構建出模塊依賴關系圖 —— ModuleGraph 對象。本文我們繼續往下&#xff0c;講解在接下來的「封裝」階段…

【大數據】—美國交通事故分析(2016 年 2 月至 2020 年 12 月)

引言 在當今快速發展的數字時代&#xff0c;大數據已成為我們理解世界、做出決策的重要工具。特別是在交通安全領域&#xff0c;大數據分析能夠揭示事故模式、識別風險因素&#xff0c;并幫助制定預防措施&#xff0c;從而挽救生命。本文將深入探討2016年2月至2020年12月期間&…

【redis】 LRU 和 LFU 算法

1、簡介 Redis 中的 LRU&#xff08;Least Recently Used&#xff09;和 LFU&#xff08;Least Frequently Used&#xff09;算法是用于決定在內存空間不足時&#xff0c;哪些鍵&#xff08;key&#xff09;應該被刪除以釋放空間的策略。這兩種算法都試圖通過跟蹤鍵的使用情況…

解決Memcached內存碎片:優化緩存性能的策略

解決Memcached內存碎片&#xff1a;優化緩存性能的策略 Memcached是一個廣泛使用的高性能分布式內存緩存系統&#xff0c;它通過在內存中緩存數據來加速數據檢索操作。然而&#xff0c;隨著時間的推移和緩存操作的進行&#xff0c;Memcached可能會遇到內存碎片問題&#xff0c…

24年河南特崗教師招聘流程+報名流程

河南特崗教師報名流程如下 1.登錄河南省特崗招聘網 登錄河南省特崗招聘網注冊賬號和密碼&#xff0c;賬號可以是手機號或者身份證號&#xff0c;密碼自己設置 2.注冊登錄賬號 注冊完賬號重新登錄賬號&#xff0c;輸入身份證號、手機號、密碼、驗證碼 3.瀏覽考試須知 填寫個人信…

Python 編程快速上手——讓繁瑣工作自動化(第2版)讀書筆記01 Python基礎快速過關

Python 編程快速上手——讓繁瑣工作自動化&#xff08;第2版&#xff09;讀書筆記01 Python基礎快速過關 1 python基礎概念 Python提供了高效的高級數據結構&#xff0c;還能簡單有效地面向對象編程。 python運算符順序 **——%——//——/——*——-——python中常見的數據…

Real-Time 3D Graphics with WebGL2

WebGL渲染管線 下圖是WebGL渲染管線的示意圖: Vertex Buffer Objects (VBOs) VBOS中包含了用于描述幾何體的信息。如&#xff0c;幾何體的頂點坐標&#xff0c;法線坐標&#xff0c;顏色&#xff0c;紋理坐標等。 Index Buffer Objects (IBOs) IBOs中包含了描述頂點關系的信…

C#的多線程UI窗體控件顯示方案 - 開源研究系列文章

上次編寫了《LUAgent服務器端工具》這個應用&#xff0c;然后里面需要新啟動一個線程去對文件進行上傳到FTP服務器&#xff0c;但是新線程里無法對應用主線程UI的內容進行更改&#xff0c;所以就需要在線程里設置主UI線程里控件信息的方法&#xff0c;于是就有了此博文。此文記…

Rocky Linux 9 快速安裝docker 教程

前述 CentOS 7系統將于2024年06月30日停止維護服務。CentOS官方不再提供CentOS 及后續版本&#xff0c;不再支持新的軟件和補丁更新。CentOS用戶現有業務隨時面臨宕機和安全風險&#xff0c;并無法確保及時恢復。由于 CentOS Stream 相對不穩定&#xff0c;剛好在尋找平替系統…

idm 支持斷點續傳嗎 idm 斷點續傳如何使用 idm斷點續傳怎么解決 idm下載中斷后無法繼續下載

斷點續傳功能&#xff0c;讓我再也不會懼怕下載大型文件。在斷點續傳的幫助下&#xff0c;用戶可以隨時暫停下載任務&#xff0c;并在空閑時繼續之前的下載進程。下載文件不懼網絡波動&#xff0c;斷點續傳讓下載過程更穩定。有關 idm 支持斷點續傳嗎&#xff0c;idm 斷點續傳如…

JavaScript:if-else類型

目錄 任務描述 相關知識 if語句 if-else語句 匹配問題 編程要求 任務描述 本關任務&#xff1a;根據成績判斷考試結果。 相關知識 在編程中&#xff0c;我們常常根據變量是否滿足某個條件來執行不同的語句。 JavaScript中利用以if關鍵字開頭的條件語句達到以上目的&am…

商城項目回顧

哈哈&#xff0c;準備期末考試去了&#xff0c;項目停了一段時間。現在又忘的差不多了。所以專門寫一篇博客總結前期項目的知識點。 Client軟件包 代碼加總結&#xff1a; 這段代碼實現了一個簡單的客戶端程序&#xff0c;用于與服務器建立連接、發送登錄信息并接收服務器的響…