Python 學習之程序與執行緒 「 上 」
程序與執行緒
程序:對於作業系統來說,一個 任務 就是一個 程序 (Process),比如開啟一個瀏覽器(任務)就是啟動一個瀏覽器程序。 程序是系統中程式執行和資源分配的基本單位 ,每個程序都有自己的資料段、程式碼段和堆疊段。
執行緒:有些程序不止同時幹一件事情,比如Word,它可以同時進行打字、拼寫檢查、列印等事情。在一個程序內部,要同時幹多件事情,就需要同時執行多個“ 子任務 ”,我們把程序內的這些“子任務”稱為執行緒(Thread), 執行緒是最小的執行單元 。
實現多工原理
現代作業系統比如 Mac OS X,UNIX,Linux,Windows 等,都支援“ 多工 ”。多工指的是作業系統可以同時執行多個任務,比如同時在聊 QQ,聽音樂,碼字…
單核CPU實現多工原理:CPU執行程式碼都是 順序 執行的,作業系統輪流讓各個任務 交替 執行,QQ 執行 2us 秒,切換到聽音樂,聽音樂執行 2us 秒,再切換到 word,執行 2us 秒…每個任務都是交替執行的,但是由於CPU的排程執行速度實在是太快了,我們感覺就像所有任務都在同時執行一樣。
多核CPU實現多工原理:真正的並行執行多工只能在多核CPU上實現,但是,由於任務數量遠遠多於CPU的核心數量,所以,作業系統也會自動把很多工輪流排程到每個核心上執行。
並行與併發
併發:看上去同時執行,任務數多於核心數
並行:真正同時執行,任務數小於等於核心數
多工的實現方式:
-
多程序模式
-
多執行緒模式
-
協程模式
-
多程序+多執行緒模式
單程序(任務)現象
程式碼塊
from time import sleep def run(): while 1: print("執行子程序") sleep(1) if __name__ == "__main__": while 1: print("執行父程序") # 父程序不結束,子程序無法開始 sleep(1) run() # 不會執行到 run 方法,只有上面的 while 迴圈結束才可以執行
執行結果

多程序(任務)現象
1.任務間互不干擾
啟動程序執行多工,各個任務間互不干擾。 multiprocessing 庫, 跨平臺 版本的多程序模組,提供了一個 Process 類來代表一個程序物件
程式碼塊
from multiprocessing import Process from time import sleep import os def run1(num): while 1: # getpid() 獲取當前程序 id 號,getppid() 獲取當前程序的父程序 id 號 print("執行子程序-%s" % num, os.getpid(), os.getppid()) sleep(2) def run2(num): while 1: print("執行子程序-%s" % num) sleep(3) if __name__ == "__main__": while 1: print("啟動父(主)程序", os.getpid()) # 建立子程序 target:程序執行的任務, args 為元組型別 p1 =Process(target=run1, args=("01",)) p1.start() # 啟動程序 p2 =Process(target=run2, args=("02",)) p2.start() # 啟動程序 while True: print("執行主程序") sleep(1)
執行結果

2. 任務間相互干擾
事實上同時執行的各個任務之間並不是沒有關聯的,而是需要相互通訊和協調,有時,任務1必須暫停等待任務 2 完成後才能繼續執行,有時,任務 3 和任務 4 又不能同時執行。所以,多程序和多執行緒的程式的複雜度要遠遠高於我們前面寫的單程序、單執行緒的程式。
程式碼塊
from multiprocessing import Process from time import sleep def run(num): print("子程序啟動") sleep(2) print("子程序結束") if __name__ == "__main__": print("父程序啟動") # 建立多個子程序,此處以一個為例 p = Process(target=run, args=("1",)) p.start() # 未加入 join() 方法時,父程序的結束不影響子程序。為了讓父程序等待所有的子程序結束後再繼續執行父程序,需要新增一個 join() 方法,通常用於程序間的同步 p.join() print("父程序結束")
執行結果

3.啟動大量子程序
以 程序池 的方式來批量建立多個子程序,體現多工的併發執行
程式碼塊
from multiprocessing import Pool import time, os, random def run(num): print("子程序 %d 啟動 -- %s " % (num, os.getpid())) start = time.time() time.sleep(random.choice([1, 2, 3])) end = time.time() print("子程序 %d 結束 -- %s -- 耗時 %.2f " % (num, os.getpid(), end-start)) if __name__ == "__main__": print("父程序啟動") # 以程序池來建立多個程序,Pool 預設大小是 CPU 核心數,表示可以同時執行的程序數量 p = Pool() for i in range(10): # 建立程序,放入程序池統一管理 p.apply_async(run, args=(i, )) # 在呼叫 join() 之前必須先呼叫 close() ,呼叫 close() 之後就不能再繼續新增新的程序了 p.close() # 程序池物件呼叫 join() ,會等待程序池中所有的子程序結束完再去執行父程序 p.join() print("父程序結束")
執行結果

4.1檔案拷貝(單程序)
當拷貝的檔案量很少時,單程序的方式比較快,因為多程序需要先建立程序花費時間;當拷貝的檔案量很大很多時,多程序的優勢就體現出來了。例子中拷貝的是大小為 446 M 的多張圖片,多程序花費的時間較少。
程式碼塊
import os,time # 實現檔案的拷貝,此處檔案大小為 446 M def copyFile(rPath, wPath): fr = open(rPath, "rb") fw = open(wPath, "wb") content = fr.read() fw.write(content) fr.close() fw.close() path = r"C:\Users\Mark\Pictures\pic1\12" toPath = r"C:\Users\Mark\Pictures\pic1\14" # 讀取 path 下的所有檔案 filesList = os.listdir(path) # 啟動 for 迴圈處理每一個檔案 start = time.time() for fileName in filesList: copyFile(os.path.join(path, fileName), os.path.join(toPath, fileName)) end = time.time() print("總耗時:%0.2f " % (end-start)) # 總耗時:19.03
4.2檔案拷貝(多程序)
程式碼塊
import os,time from multiprocessing import Pool # 實現檔案的拷貝 def copyFile(rPath, wPath): fr = open(rPath, "rb") fw = open(wPath, "wb") content = fr.read() fw.write(content) fr.close() fw.close() path = r"C:\Users\Mark\Pictures\pic1\12" toPath = r"C:\Users\Mark\Pictures\pic1\14" if __name__ == "__main__": # 讀取 path 下的所有檔案 filesList = os.listdir(path) # 啟動 for 迴圈處理每一個檔案 start = time.time() p = Pool() for fileName in filesList: p.apply_async(copyFile, args=(os.path.join(path,fileName),os.path.join(toPath,fileName))) p.close() p.join() end = time.time() print("總耗時:%0.2f " % (end - start)) # 總耗時:15.76
5.封裝程序物件
一般而言,父程序主要是負責排程子程序的執行,不具體負責某項任務,所以需要進行封裝程序物件。將實現各個功能、任務的子執行緒封裝起來,父執行緒只需負責呼叫,提高了程式碼的邏輯與整潔性。
程式碼塊
父程序呼叫程式碼
from childProcess import childProcess if __name__ == "__main__": print("父程序啟動") # 建立子程序 p = childProcess("01") # 自動呼叫 p 程序物件的 run 方法 p.start() p.join() print("父程序結束")
子程序實現功能程式碼
from multiprocessing import Process import os,time class childProcess(Process): def __init__(self, num): Process.__init__(self) self.num = num def run(self): print("子程序( %s -- %s )啟動" % (self.num, os.getpid())) # 子程序的功能 time.sleep(3) print("子程序( %s -- %s )結束" % (self.num, os.getpid()))
執行結果
