並行是今天計算機程式設計中的一項重要能力,尤其是在面對需要大量計算或I/O操作的任務時。Python 提供了多種並行的處理方式,本篇文章將深入探討其中的兩種:多執行緒與多程序,解析其使用場景、優點、缺點,並結合程式碼例子深入解讀。
Python中的執行緒是利用threading
模組實現的。執行緒是在同一個程序中執行的不同任務。
在Python中建立和啟動執行緒很簡單。下面是一個簡單的例子:
import threading
import time
def print_numbers():
for i in range(10):
time.sleep(1)
print(i)
def print_letters():
for letter in 'abcdefghij':
time.sleep(1.5)
print(letter)
thread1 = threading.Thread(target=print_numbers)
thread2 = threading.Thread(target=print_letters)
thread1.start()
thread2.start()
在這個例子中,print_numbers
和print_letters
函數都在各自的執行緒中執行,彼此互不干擾。
由於執行緒共用記憶體,因此執行緒間的資料是可以互相存取的。但是,當多個執行緒同時修改資料時就會出現問題。為了解決這個問題,我們需要使用執行緒同步工具,如鎖(Lock)和條件(Condition)等。
import threading
class BankAccount:
def __init__(self):
self.balance = 100 # 共用資料
self.lock = threading.Lock()
def deposit(self, amount):
with self.lock: # 使用鎖進行執行緒同步
balance = self.balance
balance += amount
self.balance = balance
def withdraw(self, amount):
with self.lock: # 使用鎖進行執行緒同步
balance = self.balance
balance -= amount
self.balance = balance
account = BankAccount()
特別說明:Python的執行緒雖然受到全域性直譯器鎖(GIL)的限制,但是對於IO密集型任務(如網路IO或者磁碟IO),使用多執行緒可以顯著提高程式的執行效率。
Python中的程序是通過multiprocessing
模組實現的。程序是作業系統中的一個執行實體,每個程序都有自己的記憶體空間,彼此互不影響。
在Python中建立和啟動程序也是非常簡單的:
from multiprocessing import Process
import os
def greet(name):
print(f'Hello {name}, I am process {os.getpid()}')
if __name__ == '__main__':
process = Process(target=greet, args=('Bob',))
process.start()
process.join()
由於程序不共用記憶體,因此程序間通訊(IPC)需要使用特定的機制,如管道(Pipe)、佇列(Queue)等。
from multiprocessing import Process, Queue
def worker(q):
q.put('Hello from
process')
if __name__ == '__main__':
q = Queue()
process = Process(target=worker, args=(q,))
process.start()
process.join()
print(q.get()) # Prints: Hello from process
特別說明:Python的多程序對於計算密集型任務是一個很好的選擇,因為每個程序都有自己的Python直譯器和記憶體空間,可以平行計算。
讓我們再深入地看一下concurrent.futures
模組,這是一個在Python中同時處理多執行緒和多程序的更高階的工具。concurrent.futures
模
塊提供了一個高階的介面,將非同步執行的任務放入到執行緒或者程序的池中,然後通過future物件來獲取執行結果。這個模組使得處理執行緒和程序變得更簡單。
下面是一個例子:
from concurrent.futures import ThreadPoolExecutor, as_completed
def worker(x):
return x * x
with ThreadPoolExecutor(max_workers=4) as executor:
futures = {executor.submit(worker, x) for x in range(10)}
for future in as_completed(futures):
print(future.result())
這個程式碼建立了一個執行緒池,並且向執行緒池提交了10個任務。然後,通過future物件獲取每個任務的結果。這裡的as_completed
函數提供了一種處理完成的future的方式。
通過這種方式,你可以輕鬆地切換執行緒和程序,只需要將ThreadPoolExecutor
更改為ProcessPoolExecutor
。
無論你是處理IO密集型任務還是計算密集型任務,Python的多執行緒和多程序都提供了很好的解決方案。理解它們的執行機制和適用場景,可以幫助你更好地設計和優化你的程式。
如有幫助,請多關注
個人微信公眾號:【Python全視角】
TeahLead_KrisChang,10+年的網際網路和人工智慧從業經驗,10年+技術和業務團隊管理經驗,同濟軟體工程本科,復旦工程管理碩士,阿里雲認證雲服務資深架構師,上億營收AI產品業務負責人。