15. Python 程序運行速度如何提高十倍?第一遍滾雪球學 Python 收工

      網友投稿 757 2025-04-04

      如果你有想要交流的想法、技術,歡迎在評論區留言。

      本篇文章將給大家介紹 Python 多線程與多進程相關知識,學習完該知識點之后,你的 Python 程序將進入另一個高峰。

      十五、Python 多線程與多進程

      先嘗試理解線程與進程的概念,進程范圍大,一個進程可能會包含多個線程,OK,了解到這一步就可以了,知道誰包含誰已經很不錯了,細節的地方慢慢研究。

      打開你電腦上的任務管理器,注意這里面以前說的叫做殺掉進程。

      15.1 Python 多線程

      讓我們把視角轉換一下,先從進程中抽離出來,看一下線程,在學習這部分內容的時候,這兩個概念一定不要弄錯,弄錯就翻車了。

      15.1.1 簡單的多線程

      如果一個線程只完成一個事情,那程序會變得特別呆板,例如現在你正在給編寫一段代碼,那你在編寫代碼的過程中,你使用的 IDE(代碼編輯器)就完全不能做其它事情了,必須等到編寫完所有代碼之后才可以執行其它操作,所有的事情只能一件挨著一件的做。而且在這個線程會將資源霸占住,例如讓其操作一個文件,必須等到它完成操作其它程序才可以使用,這叫做單線程。

      如何實現多線程呢,通過導入 Python 內置的 threading 模塊可以解決該問題。

      import threading # 定義一個函數,在線程中運行 def thread_work(): pass # 在 Python 中運行線程 # 建立線程對象 my_thread = threading.Thread(target=thread_work) # 啟動線程 my_thread.start()

      建立一個線程使用的是 threading 模塊中的 Thread 方法,該方法會創建一個 Thread 對象(線程對象),使用該方法的時候需要注意方法的參數值是一個函數名稱,該參數為 target,后面是線程要調用的函數名稱,沒有小括號。返回的線程對象在上述代碼中叫做 my_thread,自己定義的任意名稱都是可以的,遵循變量命名規則即可。

      線程的啟動需要調用線程對象的 start 方法。

      import threading import time # 定義一個函數,在線程中運行 def thread_work(): # 函數內部方法 print(" my_thread 線程開始工作") time.sleep(10) # 暫停十秒,為了方便模擬操作 print("時間到了,線程繼續工作") print("主線程開始運行") # 在 Python 中運行線程 # 建立線程對象 my_thread = threading.Thread(target=thread_work) # 啟動線程 my_thread.start() time.sleep(1) # 主線程停止 1 秒 print("主線程結束")

      代碼運行之后重點注意輸出的順序。

      主線程開始運行 my_thread 線程開始工作 主線程結束 時間到了,線程繼續工作

      主線程結束 輸出之后,需要等待幾秒鐘的時間,我們定義的子線程才會開始運行,即輸出 時間到了,線程繼續工作。

      15.1.2 子線程傳遞參數

      在創建線程的時候,除了直接調用某函數,也可以向子線程中的函數里傳遞參數,具體語法格式如下:

      my_thread = threading.Thread(target=函數名稱,args=['參數1','參數2',....])

      具體案例如下,像 thread_work 函數中傳遞一個 橡皮擦。

      import threading import time # 定義一個函數,在線程中運行 def thread_work(name): # 函數內部方法 print(" my_thread 線程開始工作") print("我是從主線程傳遞進來的參數:", name) time.sleep(10) # 暫停十秒,為了方便模擬操作 print("時間到了,線程繼續工作") print("主線程開始運行") # 在 Python 中運行線程 # 建立線程對象 my_thread = threading.Thread(target=thread_work, args=["橡皮擦"]) # 啟動線程 my_thread.start() time.sleep(1) # 主線程停止 1 秒 print("主線程結束")

      參數在傳遞的時候,需要與函數定義時參數匹配。多線程中不建議使用相同的變量,很容易出現問題,建議每個線程使用自己的局部變量,互相之間不要產生干擾。

      15.1.3 線程命名

      每個線程在啟動之后,如果沒有手動命名,系統會自動給其命名為 Thread-n,在程序中可以使用 currentThread().getName() 獲取線程的名稱。隨著 Python 版本的迭代,currentThread 方法已經逐步被 current_thread 替代。

      import threading import time # 定義一個函數,在線程中運行 def thread_work1(name): # 函數內部方法 print(threading.currentThread().getName()," 線程啟動") time.sleep(2) print(threading.currentThread().getName()," 線程啟動") # 定義一個函數,在線程中運行 def thread_work2(name): # 函數內部方法 print(threading.currentThread().getName(), " 線程啟動") time.sleep(2) print(threading.currentThread().getName(), " 線程啟動") print("主線程開始運行") # 在 Python 中運行線程 # 建立線程對象 my_thread1 = threading.Thread(target=thread_work1, args=["橡皮擦"]) my_thread2 = threading.Thread(target=thread_work2, args=["橡皮擦"]) # 啟動線程 my_thread1.start() # 啟動線程 my_thread2.start() time.sleep(1) # 主線程停止 1 秒 print("主線程結束")

      代碼運行結果如下,可以重點看一下線程默認的名稱。

      主線程開始運行 Thread-1 線程啟動 Thread-2 線程啟動 主線程結束 Thread-2 線程啟動 Thread-1 線程啟動

      如果想要給線程起一個獨特的名字,可以在通過 Thread 方法建立線程時,使用參數 name = "線程名稱",該名稱就是為線程單獨命名。

      import threading import time # 定義一個函數,在線程中運行 def thread_work1(name): # 函數內部方法 print(threading.currentThread().getName()," 線程啟動") time.sleep(2) print(threading.currentThread().getName()," 線程啟動") # 定義一個函數,在線程中運行 def thread_work2(name): # 函數內部方法 print(threading.currentThread().getName(), " 線程啟動") time.sleep(2) print(threading.currentThread().getName(), " 線程啟動") print("主線程開始運行") # 在 Python 中運行線程 # 建立線程對象 my_thread1 = threading.Thread(name="我是線程1(不建議用中文)",target=thread_work1, args=["橡皮擦"]) my_thread2 = threading.Thread(name="work thread",target=thread_work2, args=["橡皮擦"]) # 啟動線程 my_thread1.start() # 啟動線程 my_thread2.start() time.sleep(1) # 主線程停止 1 秒 print("主線程結束")

      除了上述辦法以外,還可以使用 currentThread().setName() 給函數命名,自己可以嘗試下哦~

      15. Python 程序運行速度如何提高十倍?第一遍滾雪球學 Python 收工

      15.1.4 Daemon 守護線程

      默認創建的線程都不是 Daemon 線程,正常情況下,一個程序建立了主線程和子線程,那程序結束需要等待所有的線程工作結束,因為如果主線程先結束了,那子線程會因為沒有可用資源而導致程序崩潰。

      如果我們希望主線程結束了,子線程自行終止,那這時就要設置一下 Daemon 線程的屬性了,設置之后,主線程若是想要結束運行,需要檢查一下 Daemon 線程的屬性。

      如果 Daemon 線程的屬性是 True,其它非 Daemon 線程執行結束,不會等待 Daemon 線程,主線程會自動結束。

      如果 Daemon 線程屬性是 False,那主線程必須等待 Daemon 線程結束才會將程序結束運行。

      以上內容翻譯成大白話就是可以把一個線程設置為 Daemon 線程,而且還可以設置一個屬性,如果屬性設置為 True,那該線程就不受重視了,其它線程結束,它就被結束了,如果設置為 False,那它就是最重要的了,主線程需要等著它結束運行,才可以進行下一步操作。

      import threading import time # 定義一個函數,在線程中運行 def thread_work1(): # 函數內部方法 print(threading.currentThread().getName()," 線程啟動") # 等待 5 秒,如果被重視,那主線程將等待,如果不被重視,很快就會執行完畢 time.sleep(5) print(threading.currentThread().getName()," 線程啟動") # 定義一個函數,在線程中運行 def thread_work2(): # 函數內部方法 print(threading.currentThread().getName(), " 線程啟動") print(threading.currentThread().getName(), " 線程啟動") print("主線程開始運行") # 在 Python 中運行線程 # 建立線程對象 my_thread1 = threading.Thread(name="我是守護線程 Daemon",target=thread_work1) my_thread1.setDaemon(True) # 先設置為 True,該線程將不被重視 my_thread2 = threading.Thread(name="work thread",target=thread_work2) # 啟動線程 my_thread1.start() # 啟動線程 my_thread2.start() print("主線程結束")

      以上代碼運行之后發現瞬間執行完畢了,并沒有等待 5 秒鐘,充分證明了不被重視的線程的處境。

      接下來修改一個屬性,可以再看一下效果。

      my_thread1.setDaemon(False)

      運行之后發現程序等待 5 秒之后才結束運行,你是否發現了其中的差異呢?

      15.1.5 堵塞主線程

      主線程在工作的時候,如果希望子線程先運行,直到該子線程運行結束,主線程才繼續工作。

      import threading import time # 定義一個函數,在線程中運行 def thread_work1(): # 函數內部方法 print(threading.currentThread().getName()," 線程啟動") time.sleep(5) print(threading.currentThread().getName()," 線程啟動") print("主線程開始運行") # 在 Python 中運行線程 # 建立線程對象 my_thread1 = threading.Thread(name="work thread",target=thread_work1) # 啟動線程 my_thread1.start() print("join 開始......") my_thread1.join() # 等待 work thead 線程運行結束 print("join 結束....") print("主線程結束")

      join 方法可以增加一個參數,該參數表示等待的秒數,當秒數到了,主線程恢復工作。

      my_thread.join(3) # 子線程運行 3 秒。

      15.1.6 is_alive 檢驗子線程是否在工作

      使用 join 方法之后,一般在后面需要加上一個 is_alive 方法,該方法會簡稱子線程是否工作結束了,如果子線程結束則返回 False,仍在工作則會返回 True。

      import threading import time # 定義一個函數,在線程中運行 def thread_work1(): # 函數內部方法 print(threading.currentThread().getName()," 線程啟動") time.sleep(5) print(threading.currentThread().getName()," 線程啟動") print("主線程開始運行") # 在 Python 中運行線程 # 建立線程對象 my_thread1 = threading.Thread(name="work thread",target=thread_work1) # 啟動線程 my_thread1.start() print("join 開始......") my_thread1.join(2) # 等待 work thead 線程運行結束 print("join 結束....") print("子線程是否仍在工作?",my_thread1.is_alive()) time.sleep(3) print("子線程是否仍在工作?",my_thread1.is_alive()) print("主線程結束")

      有的教程或者書籍中還會使用 isAlive 方法來進行判斷,這是因為 Python 版本的問題,后續建議使用 is_alive 方法。

      15.1.7 自定義線程類

      threading.Thread 是 threading 模塊內的一個類,我們可以繼承這個類,定義自己的線程類,定義的時候有兩個需要注意的地方,第一個需要在構造函數中調用 threading.Thread.__init()__ 方法,第二個是需要在類內容定義好 run 方法。

      之前的內容中,通過 threading.Thread 聲明一個線程對象時,執行 start 方法可以建立一個線程,start 方法就是在調用類中的 run 方法。

      import threading class MyThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): print(threading.Thread.getName(self)) print("橡皮擦定義好的線程") my_thread = MyThread() my_thread.run() you_thread = MyThread() you_thread.run()

      15.1.8 資源鎖定與解鎖

      在多線程程序中經常碰到多個線程使用一個共享資源的情況,為了確保共享資源在多線程共享時不出現問題,需要使用 theading.Lock 對象的兩個方法 acquire 與 release 。

      import threading my_num = 0 lock = threading.Lock() class MyThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): print(threading.Thread.getName(self)) # 調用全局變量 global my_num my_num += 10 print("現在的數字是:", my_num, "\n") # 線程列表 ts = [] # 批量創建 10 個線程 for i in range(0, 10): my_thread = MyThread() ts.append(my_thread) # 啟動 10 個線程 for t in ts: t.start() # 等待所有線程結束 for t in ts: t.join()

      以上代碼沒有使用 acquire 與 release 方法,出現的結果無規律可循,是因為各線程無法預期誰會優先取得資源,專業描述叫做 線程以不可預知的速度向前推進,當然有的地方叫做線程競速,一個意思。

      稍微修改一下就可以讓線程按照規矩執行了,在使用全局變量的時候,先鎖定資源,使用之后在釋放資源。

      # 調用全局變量 global my_num lock.acquire() my_num += 10 lock.release() print("現在的數字是:", my_num, "\n")

      以上內容如果使用 acquire 連續使用兩次就會導致死鎖。

      關于死鎖問題與資源鎖定 Threading.RLock,還有高級鎖定相關的知識,在以后的滾雪球中繼續學習,先階段掌握基本的鎖定就可以啦。

      15.1.9 未來要學習的知識

      進展到現在你已經可以實現簡單的多線程開發了,但是對于線程類的學習只揭示了最簡單的一部分,后續我們將學習到如下內容,都在第二遍滾雪球時學習。

      queue 模塊,也叫做隊列模塊

      Semaphore 信號量,高級鎖機制

      Barrier 柵欄

      Event 線程通訊機制

      15.2 subprocess 模塊

      subprocess 是 Python 中用于建立子進程的模塊,注意是子進程。導入該模塊使用 import subprocess。

      15.2.1 Popen 方法

      該方法可以打開計算機內部的應用程序,也可以打開自己寫好的程序,文件路徑寫對即可。

      import subprocess # 打開計算機 calc_pro = subprocess.Popen('calc.exe') # 打開畫板 mspaint_pro = subprocess.Popen('mspaint.exe')

      打開的子進程,主程序已經結束了。

      15.2.2 Popen 方法攜帶參數

      可以在 Popen 方法打開程序的時候,傳遞一個參數進去,該參數為列表類型,第一個元素是要打開的應用程序,第二個則是傳遞進去的文件。

      例如打開畫圖程序。

      import subprocess # 打開計算機 # calc_pro = subprocess.Popen('calc.exe') # 打開畫板 mspaint_pro = subprocess.Popen(['mspaint.exe','./pic.jpg'])

      文件的路徑不要寫錯,以上代碼會打開畫板程序并且在畫板打開一個圖片。

      15.2.3 通過 start 打開程序

      在電腦上通過雙擊就可以打開某種文件,這是因為 Windows 系統已經給我們做好了關聯,那能不能在 Python 中也模擬出該方式呢,很簡單,通過 subprocess.Popen 方法的參數即可實現。

      import subprocess # 打開圖片 mspaint_pro = subprocess.Popen(['start','./pic.jpg'],shell = True)

      使用該代碼打開圖片是使用你默認的圖片預覽程序,滿足了剛才所說的場景。該方法核心使用的有兩個地方一個是原程序位置使用的是 start 關鍵字(僅在 Windows 上有效),第一個是 shell = True 參數。

      15.2.4 通過 run 方法調用子進程

      該方法屬于新增方法,通過 subprocess.run 方法即可調用子進程。具體內容可以自行嘗試即可。

      15.3 這篇博客的總結

      本篇博客主要內容是 Python 的多線程應用,順帶著說了一點點關于進程的相關知識,對于多線程,很多學習 Python 很久的同學都不一定可以搞清楚,在這里希望大家第一次學習先有概念支撐即可,能掌握多少在本階段不重要,學習是需要時間積累的,一遍就會那是天才或者是吹牛的,有很多工作 2~3 年的還不一定能把多線程多進程說清楚呢,所以不要著急哦,繼續往后面看,往后面學就好了。

      第一遍滾雪球學 Python 收官。下期見。

      博主 ID:夢想橡皮擦,希望大家

      評論

      、

      。

      Python 任務調度

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:EXCEL隱藏表格表的方法
      下一篇:word2010怎么繪制三線表(word里如何繪制三線表)
      相關文章
      中国china体内裑精亚洲日本| 亚洲Av无码一区二区二三区| 亚洲国产成人精品无码区二本| 亚洲制服丝袜精品久久| 亚洲精品国产成人99久久| 亚洲色成人WWW永久网站| 美腿丝袜亚洲综合| 中文字幕在亚洲第一在线| 亚洲人成网站观看在线播放| 亚洲精品无码99在线观看| 亚洲国产专区一区| 久久亚洲国产成人影院网站| 国外亚洲成AV人片在线观看| 狠狠色婷婷狠狠狠亚洲综合 | 亚洲国产精品激情在线观看| 国产亚洲福利一区二区免费看| 久久久久久亚洲精品无码| 国产成人不卡亚洲精品91| 国产产在线精品亚洲AAVV| 亚洲第一区精品观看| 亚洲国产成人精品女人久久久| 亚洲国产精品视频| 中文亚洲AV片在线观看不卡| 亚洲午夜久久久久久久久久 | 亚洲乱码一二三四区乱码| 亚洲中文字幕无码久久2020| 亚洲AV无码XXX麻豆艾秋| 国产亚洲精品美女2020久久| 亚洲国产精品综合久久网络| 亚洲午夜精品久久久久久浪潮| 黑人大战亚洲人精品一区| 好看的电影网站亚洲一区| 亚洲A∨无码一区二区三区 | 久久精品九九亚洲精品| 亚洲性无码av在线| 亚洲人成电影网站色| 亚洲AV中文无码乱人伦| 亚洲精品线路一在线观看| 亚洲乱码精品久久久久..| 亚洲精品人成在线观看| 亚洲同性男gay网站在线观看|