Python多線程編程入門詳解
一、任務、進程和線程
現代操作系統比如Mac OS X
, Linux
,Windows
等,都是支持“多任務”的操作系統。
什麼叫“多任務”(multitasking
)呢?簡單地說,就是操作系統可以同時運行多個任務。例如你一邊在用瀏覽器上查資料,一邊在聽MP3,一邊在用Word寫文檔,這就是多任務。
打開Windows的任務管理器,可以直觀的瞭解一下:
任務
(task)是最抽象的,是一個一般性的術語,指由軟件完成的一個活動。一個任務既可以是一個進程,也可以是一個線程。簡而言之,它指的是一系列共同達到某一目的的操作。例如,讀取數據並將數據放入內存中。這個任務可以作為一個進程來實現,也可以作為一個線程(或作為一個中斷任務)來實現。
單核CPU如何實現“多任務”
三個任務A,B,C在單個CPU上交替執行,邏輯上表現為三個執行實例並發執行,但實質物理上是串行執行。
多核CPU如何實現“多任務”
多任務的真正並行隻能在多核CPU上實現,但是,一般而言,任務的數量多於CPU的核數,還是需要將多個任務輪流調度到每個核上執行。
單核與多核處理器(Multi-core processor
)示例
多核CPU:簡單地說是將多個核心裝載一個封裝裡,外觀上看是一個處理器。
串行:一個處理完再一個。
並行:指兩個或者多個事件在同一時刻發生。
並發:指兩個或多個事件在同一時間間隔內發生。
例如:並發時你正在吃飯,吃到一半電話響,去接電話,接完後繼續吃飯;並行時邊吃飯邊打電話。
進程
(process)常常被定義為程序的執行。可以把一個進程看成是一個獨立的程序,在內存中有其完備的數據空間和代碼空間。一個進程所擁有的數據和變量隻屬於它自己。
線程
(tread)則是某一進程中一路單獨運行的程序。也就是說,線程存在於進程之中。一個進程由一個或多個線程構成,各線程共享相同的代碼和全局數據 ,但各有其自己的堆棧。由於堆棧是每個線程一個,所以局部變量對每一線程來說是私有的 。由於所有線程共享同樣的代碼和全局數據,它們比進程更緊密,比單獨的進程間更趨向於相互作用,線程間的相互作用更容易些,因為它們本身就有某些供通信用的共享內存:進程的全局數據。
進程和線程的關系
(1)一個線程隻能屬於一個進程,而一個進程可以有多個線程,但至少有一個線程。
(2)資源分配給進程,同一進程的所有線程共享該進程的所有資源。
(3)處理機分給線程,即真正在處理機上運行的是線程。
(4)線程在執行過程中,需要協作同步。不同進程的線程間要利用消息通信的辦法實現同步。
二、Python既支持多進程,又支持多線程
multiprocessing — 基於進程的並行 — Python 3.10.0 文檔
threading — 基於線程的並行 — Python 3.10.0 文檔
Python實現多進程
在Unix/Linux下,Mac系統是基於BSD(Unix的一種)內核,都可以使用fork()調用實現多進程,Python的os模塊封裝瞭常見的系統調用,其中就包括fork。例如:
import os print('Process (%s) start...' % os.getpid()) # Only works on Unix/Linux/Mac: pid = os.fork() #Windows中不支持fork(),將報錯 if pid == 0: print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid())) else: print('I (%s) just created a child process (%s).' % (os.getpid(), pid))
需要註意的是,上述代碼在Unix/Linux、Mac系統中可以運行,但在Windows中運行報錯:
AttributeError: module ‘os’ has no attribute ‘fork’
因為Windows沒有fork調用,怎麼辦?
multiprocessing模塊是跨平臺版本的多進程模塊。在Windows上用Python編寫多進程的程序,可以使用multiprocessing模塊。multiprocessing模塊提供瞭一個Process類來代表一個進程對象。
Process進程類的說明
Process([group [, target [, name [, args [, kwargs]]]]])
group
:指定進程組,目前隻能使用Nonetarget
:執行的目標任務名name
:進程名字,默認為Process-N,N為從1開始遞增的整數args
:以元組方式給執行任務傳參kwargs
:以字典方式給執行任務傳參
Process創建的實例對象的常用方法:
start()
:啟動子進程實例(創建子進程)join()
:等待子進程執行結束terminate()
:不管任務是否完成,立即終止子進程
Process創建的實例對象的常用屬性:
例如:
import multiprocessing import time # 跳舞任務 def dance(): for i in range(3): print("跳舞中...") time.sleep(0.2) # 唱歌任務 def sing(): for i in range(3): print("唱歌中...") time.sleep(0.2) dance_process = multiprocessing.Process(target=dance(), name="myprocess1") sing_process = multiprocessing.Process(target=sing()) # 啟動子進程執行對應的任務 dance_process.start() sing_process.start()
執行結果如下圖:
Python實現多線程
多任務可以由多進程完成,也可以由一個進程內的多線程完成。
Python的標準庫提供瞭兩個模塊:_thread
和threading
,_thread是低級模塊,threading是高級模塊,對_thread進行瞭封裝。絕大多數情況下,使用threading這個高級模塊。
線程類Thread
Thread([group [, target [, name [, args [, kwargs]]]]])
group
: 線程組,目前隻能使用Nonetarget
: 執行的目標任務名args
: 以元組的方式給執行任務傳參kwargs
: 以字典方式給執行任務傳參name
: 線程名,一般不用設置
Thread類提供瞭以下方法:
run()
: 用以表示線程活動的方法。start()
:啟動線程活動。join([time])
: 等待至線程中止。這阻塞調用線程直至線程的join() 方法被調用中止-正常退出或者拋出未處理的異常-或者是可選的超時發生。isAlive()
:getName()
: 返回線程名。setName()
: 設置線程名。
啟動一個線程就是把一個函數傳入並創建Thread實例,然後調用start()開始執行,例子:
import time, threading # 新線程執行的代碼: def loop(): print('thread %s is running...' % threading.current_thread().name) n = 0 while n < 5: n = n + 1 print('thread %s >>> %s' % (threading.current_thread().name, n)) time.sleep(1) print('thread %s ended.' % threading.current_thread().name) print('thread %s is running...' % threading.current_thread().name) t = threading.Thread(target=loop, name='LoopThread') t.start() t.join() print('thread %s ended.' % threading.current_thread().name)
執行結果如下圖:
總結
本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!
推薦閱讀:
- 手把手帶你瞭解python多進程,多線程
- Python threading和Thread模塊及線程的實現
- Python學習筆記之線程
- Python解析器Cpython的GIL解釋器鎖工作機制
- 深入瞭解Python的多線程基礎