Python threading 模組提供高階執行緒介面,但受限於全域解譯器鎖(GIL),CPU 密集型任務無法真正並行。本文涵蓋執行緒建立、共享資料競態條件、鎖機制、GIL 影響與替代方案。
import threading, time
def loop():
print('thread %s is running...' % threading.current_thread().name)
for n in range(1, 6):
print('thread %s >>> %d' % (threading.current_thread().name, n))
time.sleep(1)
print('thread %s ended.' % threading.current_thread().name)
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print('thread %s ended.' % threading.current_thread().name)
MainThread。join() 等待執行緒結束。
balance = 0
def change_it(n):
global balance
balance += n
balance -= n
def run_thread(n):
for _ in range(100000):
change_it(n)
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start(); t2.start()
t1.join(); t2.join()
print(balance) # 可能非 0
因 balance += n 非原子操作(讀取、計算、寫入),多執行緒交替執行導致資料錯誤。
lock = threading.Lock()
def run_thread(n):
for _ in range(100000):
lock.acquire()
try:
change_it(n)
finally:
lock.release()
acquire() 取得鎖,release() 釋放鎖。try/finally 確保釋放,避免死結。
import threading, multiprocessing
def cpu_loop():
x = 0
while True:
x ^= 1
for _ in range(multiprocessing.cpu_count()):
threading.Thread(target=cpu_loop).start()
啟動 N 條死迴圈執行緒(N = 核心數),觀察 CPU 使用率僅約 100~170%,遠低於理論值。
| 需求 | 方案 |
|---|---|
| CPU 密集型並行 | 使用 multiprocessing 多進程(各自獨立 GIL) |
| I/O 密集型並行 | threading、asyncio、concurrent.futures 均可 |
| 需 C 擴充模組繞過 GIL | 撰寫 C/C++ 擴充(如 NumPy、Pandas 內部實作) |