← Back to list

Effective python note chapter 7

第七章: Concurrency and Parallelism

郭政旻 (Nick Kuo) · 2020-09-10 01:44 · 0 claps · 9.4 min read
#effective-python #python
Open on Medium ↗

Effective python note chapter 7

*第七章: Concurrency and Parallelism*

# Item 52: 使用 subprocess 來管理 child processes

  • 使用 subprocess module 來執行 child processes, 並管理 input output streams
  • Child process 平行的執行, 最大化 CPU 多核心的資源使用率
  • 用 popen class 做更進階的使用, 例如 UNIX-tyle pipelines
  • 使用 communicate method 的 timeout 參數, 防止 deadlocks

# Item 53: 使用 thread 做 blocking I/O, 避免 parallelism

  • 由於 global interpreter lock (GIL), 在多核心中 python threads 沒辦法平行的處理
  • 儘管如此, threads 還是很有用的, 提供簡單的方式, 讓程式看起來在同一時間做許多事情
  • 使用 python threads 可以平行的執行 system call, 這讓你可以在計算的同時可以執行 blocking I/O (blocking I/O 包含了像是讀寫檔案、與網路互動、與裝置通訊等等的工作。執行緒能夠協助我們處理阻斷式I/O,讓我們的程式不必等候OS回應我的請求時間。)

# Item 54: 在 threads 中, 使用 lock 來防止 data race

  • 即便有 GIL 限制, 還是需要注意 threads 間的data race
  • 如果沒有使用 mutual-exclusion lock (mutexes), 讓多個 threads 去修改同個 objects, 資料就會出錯, 這時可以使用 lock 來防止
from threading import Thread, Lock
class LockingCounter:
    def __init__(self):
        self.lock = Lock()
        self.count = 0
    def increment(self, offset):
        with self.lock:
            self.count += offset
def worker(sensor_index, how_many, counter):
    for _ in range(how_many):
        counter.increment(1)
how_many = 10 ** 5
threads = []
counter = LockingCounter()
for i in range(5):
    thread = Thread(target=worker, args=(i, how_many, counter))
    threads.append(thread)
    thread.start()
for thread in threads:
    thread.join()
expected = how_many * 5
found = counter.count
print(f'Counter should be {expected}, got {found}')
>>>
Counter should be 500000, got 500000

# Item 55: 使用 queue 來調來調節 threads 間的工作

  • 用 python threads 時, pipeline 對於組織有順序的工作是不錯的, 特別是對於 I/O-bound 程式
  • 特別留意 Concurrent pipeline 會遇到的問題, 例如 busy waiting, 何時告訴 worker 要停止 及 潛在記憶體巨變
  • Queue class 提供了所需要的工具讓你構建穩固的 pipelines, blocking operations, buffer sizes, and joining
from queue import Queue
from threading import Thread
class ClosableQueue(Queue):
    SENTINEL = object()
    def close(self):
        self.put(self.SENTINEL)
    def __iter__(self):
        while True:
            item = self.get()
            try:
                if item is self.SENTINEL:
                    return  # Cause the thread to exit
                yield item
            finally:
                self.task_done()
class StoppableWorker(Thread):
    def __init__(self, func, in_queue, out_queue):
        super().__init__()
        self.func = func
        self.in_queue = in_queue
        self.out_queue = out_queue
    def run(self):
        for item in self.in_queue:
            result = self.func(item)
            self.out_queue.put(result)
def download(item):
    return item
def resize(item):
    return item
def upload(item):
    return item
download_queue = ClosableQueue()
resize_queue = ClosableQueue()
upload_queue = ClosableQueue()
done_queue = ClosableQueue()
threads = [
    StoppableWorker(download, download_queue, resize_queue),
    StoppableWorker(resize, resize_queue, upload_queue),
    StoppableWorker(upload, upload_queue, done_queue),
]
for thread in threads:
    thread.start()
for _ in range(1000):
    download_queue.put(object())
download_queue.close()
download_queue.join()
resize_queue.close()
resize_queue.join()
upload_queue.close()
upload_queue.join()
print(done_queue.qsize(), 'items finished')

# Item 56: 可以辨別在什麼情況下, 需要使用 concurrency

  • 當程式越來越複雑時, 會需要 multiple concurrent
  • 常見的 concurrency coordination 種類為 fan-out (產生新的 concurrency units) 及 fan-in(等待現有的 concurrency units 完成)

# Item 57: 避免當有請求時, 就一昧建立新的 thread

  • Threads 有許多不利的地方, 若需要使用許多 threads, 需耗費較多資源及記憶體並且要依靠特殊的工具來協調, 比如 Lock
  • Threads 內建沒有提供 raise exceptions 處理機制, 造成其造成出錯或是等待其他 threads 時, 難以 debug

# Item 58: 當需要使用 concurrency 重構時, 了解如何使用 queue

  • 使用 queue 搭配固定的固定的 worker threads 數量, 改善 fan-out 及 fan-in 的可擴展性
  • 用 queue 來重構現有的程式碼, 尤其是需要多個階段的pipeline 時
  • 與其他內建的 python 功能模塊相比, 使用 queue 從根本上限制了 I/O 的並行總數

# Item 59: 當 threads 需要 concurrency 時, 考慮 ThreadPoolExecutor

  • ThreadPoolExecutor 通過有限制的重構, 實現簡單的 I/O 並行, 避免每次 fanout concurrency 時, thread 的啟動成本
  • 儘管 ThreadPoolExecutor 消除了直接使用 threads 可能造成的記憶體記憶體問題, 但也須預先指定 max_workers 來限制 I/O 並行性

# Item 60: 使用 coroutines 達到高 conurrent I/O

  • Function 中使用關鍵字 async, 稱為 coroutines, 使用關鍵字 await, caller 可以從獨立的 coroutine 收到結果
  • Coroutines 提供非常有效率的方式,看起來像同一時間執行數千個 functions
  • Coroutines 可以使用 fan-out 及 fan-in 達到平行的 I/O, 同時克服了使用 threads 時產生的問題

# Item 61: 知道如何從 thread I/O 轉成 asyncio

  • Python 提供了非同步的 for loops, with statements, generators, comprehensions, and library helper functions, 可以用做 coroutine 的替代品
  • 內建的 asyncio module, 使原本使用 threading 及 blocking I/O 可以直覺的轉換成 coroutine, asynchronous I/O

# Item 62: 混合 threads 及 coroutines 減少轉換到 asyncio 的過渡期

  • Asyncio event loop 裡的 awaitable run_in_executor method 使 coroutines 可以在 threadpoolexecutor pools 執行同步的 functions. 這有助於從上到下搬遷到使用 asyncio
  • Asyncio event loop 裡的 run_until_complete method 使同步的程式可以執行 coroutine 直到完成. asyncio.run_coroutine_threadsafe 函數提供跨線程邊界的相同 functions. 有助於從下到上的搬遷

# Item 63: 避免 blocking asyncio event loop 來達到最大的響應

  • 在 coroutine 中進行系統調用(包含 blocking I/O 及啟動線程), 可以降低程式的響應速度
  • 傳遞 debug = True 的參數給 asyncio.run, 以便檢測何時有 coroutines preventing the event loop, 並快速做出反應

# Item 64: 為了真正的平行, 考慮 concurrent.futures

  • 將 CPU 瓶頸轉移到使用 C-extension modules, 可以最大程度的提升 python 程式效能, 然而成本很高且容易產生 bugs
  • Multiprocessing module 提供了強大的功能, 可以平行的執行某些類型的計算
  • 最好通過內建的 concurrent.future module 及其 ProcessPoolExecutor class 來使用 multiprocessing
  • 在用盡其他方法前, 最後在使用 multiprocessing module

有問題或是有錯誤的地方, 歡迎留言與我討論, 謝謝您。

[embed]Previous article: Effective python note chapter 6 第六章: Metaclasses and Attributessmedium.com

[embed]Next article: Effective python note chapter 8 第八章: Robustness and Performancemedium.com


메타데이터
post_id
c3e27c5e5ed5
slug
effective-python-note-chapter-7-c3e27c5e5ed5
url
https://medium.com/@nickest14/effective-python-note-chapter-7-c3e27c5e5ed5
canonical_url
https://medium.com/@nickest14/effective-python-note-chapter-7-c3e27c5e5ed5
author_url
https://medium.com/@nickest14
status
ok
fetched_at
2026-07-15 22:34:01