FA-TOOLS — Header Component

کدهای آماده multiprocessing در پایتون

پایتون یکی از محبوب‌ترین زبان‌های برنامه‌نویسی دنیاست، اما وقتی صحبت از کارهای سنگین محاسباتی (CPU-bound) می‌شود، وجود محدودیت بزرگی به نام GIL (Global Interpreter Lock) مانع استفاده کامل از تمام هسته‌های پردازنده می‌شود. اینجاست که ماژول قدرتمند multiprocessing به کمک ما می‌آید. با استفاده از این کدهای آماده پایتون، می‌توانید پردازنده‌های چند هسته‌ای خود را به خدمت بگیرید و سرعت اجرای برنامه‌هایتان را تا چند برابر افزایش دهید.

راهنمای سریع: چه زمانی از Multiprocessing استفاده کنیم؟

⚡ محاسبات سنگین (CPU-Bound)

پردازش تصویر، کارهای ریاضی سنگین، فشرده‌سازی داده‌ها و رمزنگاری. راهکار ایده آل: Multiprocessing

🌐 کارهای شبکه و دیسک (I/O-Bound)

دانلود فایل، درخواست‌های API، خواندن و نوشتن روی دیسک. راهکار ایده آل: Multithreading / Asyncio

چرا در پایتون به Multiprocessing نیاز داریم؟

کدهای آماده multiprocessing در پایتون — تصویر 1

برخلاف بسیاری از زبان‌های برنامه‌نویسی مانند سی‌پلاس‌پلاس یا جاوا، مفسر استاندارد پایتون (CPython) از قابلیتی به نام GIL استفاده می‌کند. جی‌آی‌ال تضمین می‌کند که در هر لحظه فقط یک ترد (Thread) کدهای پایتون را اجرا کند. این مکانیسم برای مدیریت حافظه پایتون بسیار حیاتی است، اما یک جنبه منفی دارد: استفاده از سیستم‌های چند رشته‌ای (Multithreading) برای کارهای محاسباتی سنگین، سرعت برنامه شما را افزایش نمی‌دهد!

کتابخنه multiprocessing با ایجاد فرآیندهای کاملاً مجزا (Processes) به جای تردها، این محدودیت را دور می‌زند. هر فرآیند در پایتون مفسر و فضای حافظه مخصوص به خود را دارد؛ بنابرین هر پردازه می‌تواند روی یکی از هسته‌های CPU شما به طور همزمان و واقعی اجرا شود. اگر به دنبال پیاده‌سازی رابط کاربری با سرعت بالا هستید، می‌توانید از ساختار کدهای فرانت‌اند مانند تکه‌کدهای HTML و استایل‌های بهینه نیز در پروژه‌های وب خود در کنار پردازش بک‌اند پایتون استفاده کنید.

تفاوت Multiprocessing و Multithreading در یک نگاه

کدهای آماده multiprocessing در پایتون — تصویر 2

بسیاری از برنامه‌نویسان تازه کار تفاوت این دو مفهوم را به درستی درک نمی‌کنند. جدول زیر به ساده‌ترین شکل ممکن تفاوت‌های کلیدی را برای شما شفاف می‌کند:

ویژگی / معیار مقایسه چندپردازشی و چندرشته‌ای
مفهوم اصلی Multiprocessing: ایجاد پروسه‌های مجزا با حافظه جداگانه.
Multithreading: ایجاد رشته‌های متعدد درون یک پروسه با حافظه مشترک.
تاثیر GIL Multiprocessing: کاملاً بی اثر است و هسته‌ها را آزاد می‌کند.
Multithreading: تحت تاثیر شدید GIL است و موازی‌سازی واقعی محاسبات را قفل می‌کند.
مصرف منابع سیستم Multiprocessing: مصرف رم بالا به دلیل تخصیص حافظه جداگانه برای هر پروسه.
Multithreading: سبک و کم‌مصرف به دلیل اشتراک حافظه.
بهترین کاربرد Multiprocessing: پردازش‌های ریاضی فشرده، یادگیری ماشین و رندرینگ فیلم.
Multithreading: کارهای شبکه، وب‌اسکرپینگ سبک و ارتباط با دیتابیس.

کدهای آماده و کاربردی Multiprocessing

کدهای آماده multiprocessing در پایتون — تصویر 3

در این بخش، کاربردی‌ترین و بهینه‌ترین الگوهای برنامه‌نویسی موازی در پایتون را آماده کرده‌ایم. شما می‌توانید این کدها را کپی کرده و مستقیماً در پروژه‌های خود استفاده کنید. تمامی این ابزارها بخشی از آرشیو جامع تکه کدهای کاربردی برای توسعه‌دهندگان وب و نرم‌افزار هستند.

۱. فرمول پایه با کلاس Process

ساده‌ترین روش برای شروع کار با فرآیندهای موازی، استفاده از کلاس Process است. در این متد ما فرآیندها را به صورت دستی تعریف، راه‌اندازی و کنترل می‌کنیم.

import multiprocessing
import time

def worker_task(name, delay):
    print(f"فرآیند {name} شروع شد.")
    time.sleep(delay)
    print(f"فرآیند {name} پس از {delay} ثانیه به پایان رسید.")

if __name__ == "__main__":
    # ایجاد فرآیندهای مختلف با آرگومان‌های متفاوت
    process1 = multiprocessing.Process(target=worker_task, args=("A", 2))
    process2 = multiprocessing.Process(target=worker_task, args=("B", 4))

    # شروع فرآیندها
    process1.start()
    process2.start()

    # منتظر ماندن برای تمام شدن فرآیندها قبل از پایان برنامه اصلی
    process1.join()
    process2.join()

    print("تمام فرآیندها با موفقیت انجام شدند.")
نکته بسیار مهم برای کاربران ویندوز و مک: همیشه کدهای مربوط به استارت کردن فرآیندهای جدید را درون بلوک شرطی if __name__ == '__main__': قرار دهید. عدم رعایت این نکته باعث بروز خطاهای پی‌درپی و اجرای بی‌نهایت کدهای شما می‌شود.

۲. مدیریت خودکار پردازش‌ها با ابزار فوق‌العاده Pool

وقتی با تعداد زیادی ورودی مواجه هستید، تعریف دستی تک‌تک پروسه‌ها کاری غیرمنطقی است. در این حالت کلاس Pool ورودی‌های شما را به صورت خودکار بین هسته‌های پردازنده توزیع می‌کند و خروجی‌ها را جمع‌آوری می‌نماید.

from multiprocessing import Pool
import time

def square_number(n):
    # یک محاسبه فرضی سنگین
    time.sleep(0.5)
    return n * n

if __name__ == "__main__":
    numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    
    start_time = time.time()
    
    # ساخت یک استخر پردازشی؛ به صورت خودکار از حداکثر هسته‌های موجود استفاده می‌کند
    with Pool() as pool:
        # متد map متغیرها را به ترتیب پردازش کرده و نتیجه را لیست می‌کند
        results = pool.map(square_number, numbers)
        
    print(f"نتایج محاسبات موازی: {results}")
    print(f"زمان کل اجرا: {time.time() - start_time:.2f} ثانیه")

۳. استفاده از Queue برای تبادل داده بین فرآیندها

از آنجا که فرآیندها حافظه مشترکی ندارند، نمی‌توانید از متغیرهای معمولی یا سراسری برای جابه‌جایی اطلاعات بین آن‌ها استفاده کنید. امن‌ترین روش برای ارسال پیام‌ها و داده‌ها، استفاده از شیء Queue (صف) است که از سیستم‌های همگام‌سازی داخلی بهره می‌برد.

import multiprocessing

def producer(queue, items):
    for item in items:
        print(f"تولید کننده: ارسال {item}")
        queue.put(item) # قرار دادن داده در صف
    queue.put(None)  # سیگنال برای پایان دادن به کار مصرف‌کننده

def consumer(queue):
    while True:
        item = queue.get() # دریافت داده از صف
        if item is None:
            break
        print(f"مصرف کننده: دریافت و پردازش {item}")

if __name__ == "__main__":
    my_queue = multiprocessing.Queue()
    data = ["پکیج ۱", "پکیج ۲", "پکیج ۳", "پکیج ۴"]

    process_prod = multiprocessing.Process(target=producer, args=(my_queue, data))
    process_cons = multiprocessing.Process(target=consumer, args=(my_queue,))

    process_prod.start()
    process_cons.start()

    process_prod.join()
    process_cons.join()

بهینه‌سازی پروژه‌های واقعی با کدهای آماده

استفاده از چندپردازشی در سناریوهای واقعی زمانی جذاب می‌شود که به سراغ پردازش فایل‌های بزرگ یا توسعه ابزارهای وب می‌رویم. به عنوان مثال، اگر قصد توسعه یک سیستم خودکارسازی کارآمد در بستر مدیریت محتوا دارید، استفاده از چندپردازشی در کنار ابزارهای توسعه یافته با توابع وردپرس می‌تواند سرعت فرآیندهایی نظیر فشرده‌سازی انبوه تصاویر آپلود شده را به شدت ارتقا دهد.

همچنین برای طراحان وب که به شبیه‌سازی رفتارهای کاربر یا رندر گرفتن‌های پیچیده نیاز دارند، هماهنگی کامل بین قطعات بک‌اند با کدهای جاوا اسکریپت در مرورگر و چندپردازشی پایتون در سرور، بهترین تجربه کاربری ممکن را پدید می‌آورد. به علاوه برای استایل‌دهی بهتر به داشبوردهای مدیریتی کدهای پایتون خود، یادگیری و استفاده از الگوهای آماده در بخش استایل‌های CSS بسیار کارآمد خواهد بود.

عیب‌یابی سریع و رفع خطاهای رایج

بسیاری از برنامه‌نویسان در زمان پیاده‌سازی کدهای مالتی‌پروسسینگ با خطاهای گیج‌کننده روبه‌رو می‌شوند. در اینجا شایع‌ترین مشکلات و راه‌حل قطعی آن‌ها را بررسی می‌کنیم:

  • خطای PicklingError در پایتون:

    علت: پایتون قبل از ارسال داده‌ها به فرآیندهای دیگر باید آن‌ها را سریال‌سازی (Serialize) کند. برخی اشیاء یا توابعی که به شکل محلی تعریف شده‌اند قابل پیکسل شدن نیستند.

    راه‌حل: توابع هدف خود را همیشه در بالاترین سطح فایل تعریف کنید و از تعریف توابع تودرتو یا Lambda برای فرآیندهای موازی خودداری فرمایید.
  • مصرف بیش از حد حافظه RAM:

    علت: زمانی رخ می‌دهد که تعداد زیادی فرآیند به صورت همزمان ایجاد می‌شوند و داده‌های حجیمی را لود می‌کنند.

    راه‌حل: همیشه از محدود کردن حداکثر ظرفیت استخر پردازش خود استفاده کنید. مثلاً به جای استفاده از کل هسته‌ها، بنویسید Pool(processes=4) تا منابع سیستم قفل نکنند.
  • بن‌بست یا Deadlock:

    علت: زمانی که از Queueها استفاده می‌کنید و داده‌ها بدون خوانده شدن در صف باقی می‌مانند، مفسر قفل می‌کند.

    راه‌حل: مطمئن شوید که فرآیند مصرف‌کننده قبل از فراخوانی متد join() بر روی فرآیندها، تمام آیتم‌های صف را خوانده و خالی کرده باشد.

سوالات متداول کاربران (FAQ)

آیا Multiprocessing همیشه سرعت برنامه‌های پایتون را افزایش می‌دهد؟

خیر، اگر کار شما از نوع ورودی/خروجی (مانند درخواست‌های اینترنتی یا خواندن اطلاعات از هارد) باشد، هزینه‌ی ساخت فرآیندهای مجزا باعث کندتر شدن اجرای برنامه می‌شود. در این موارد بهتر است از Asyncio یا Multithreading استفاده کنید.

چگونه می‌توانم تعداد هسته‌های پردازنده‌ام را در پایتون متوجه شوم؟

شما می‌توانید با استفاده از دستور ساده‌ی multiprocessing.cpu_count() تعداد کل هسته‌های منطقی در دسترس سیستم خود را خروجی بگیرید.

تفاوت اصلی بین متدهای map و apply در کلاس Pool چیست؟

متد map یک تابع را بر روی لیستی از آیتم‌ها به صورت تکرار شونده اعمال می‌کند، در حالی که متد apply برای ارسال یک کار تکی به استخر پردازش و مسدودسازی روند تا گرفتن نتیجه استفاده می‌شود.

Table of Contents

آخرین نوشته‌ها