Threading در مقابل Multiprocessing
در پایتون، Threading چندین نخ را در یک پردازه اجرا میکند اما به دلیل GIL (Global Interpreter Lock) تنها یک نخ در هر لحظه بایتکد پایتون اجرا میکند؛ Multiprocessing با ایجاد پردازههای مستقل، هرکدام با مفسر و حافظه جداگانه، امکان اجرای واقعی موازی روی چند هسته CPU را فراهم میکند.
| ویژگی | Threading | Multiprocessing |
|---|---|---|
| مدل اجرا | چند نخ در یک پردازه (حافظه مشترک) | چند پردازه مستقل (حافظه جدا) |
| تاثیر GIL | محدود به یک نخ در هر لحظه | هر پردازه GIL مستقل خودش را دارد |
| سربار ایجاد | کم (سبکوزن) | زیاد (کپی مفسر و حافظه) |
| مناسب برای | عملیات I/O-bound | عملیات CPU-bound |
کِی از Threading استفاده کنیم؟
وقتی کار شما I/O-bound است (درخواست شبکه، خواندن فایل، پرسوجوی دیتابیس) از Threading استفاده کنید؛ چون در زمان انتظار برای I/O، GIL آزاد میشود و نخهای دیگر میتوانند اجرا شوند. سربار حافظه و ایجاد آن نیز بسیار کمتر از Multiprocessing است.
کِی از Multiprocessing استفاده کنیم؟
وقتی کار شما CPU-bound است (محاسبات سنگین، پردازش تصویر، عملیات ریاضی) از Multiprocessing استفاده کنید؛ چون هر پردازه GIL مجزای خودش را دارد و میتواند واقعاً روی هستههای مختلف CPU بهصورت موازی اجرا شود.
مثال Threading
import threading, requests
def fetch(url):
requests.get(url)
threads = [threading.Thread(target=fetch, args=(u,)) for u in urls]
[t.start() for t in threads]
[t.join() for t in threads]
مثال Multiprocessing
from multiprocessing import Pool
def square(n):
return n * n
with Pool(processes=4) as pool:
results = pool.map(square, range(1000000))
اشتباه رایج
بسیاری از توسعهدهندگان برای تسریع محاسبات سنگین CPU-bound از Threading استفاده میکنند و انتظار بهبود کارایی دارند، در حالی که به دلیل GIL هیچ افزایش سرعتی حاصل نمیشود (و گاهی به دلیل سربار جابهجایی نخها حتی کندتر هم میشود). راهحل درست استفاده از Multiprocessing یا کتابخانههایی مثل NumPy است که GIL را در سطح C آزاد میکنند.
جمعبندی
قانون سریع: I/O-bound → Threading (یا asyncio)، CPU-bound → Multiprocessing.
