۱ دی ۱۴۰۳

Techboy

اخبار و اطلاعات روز تکنولوژی

همزمانی و موازی پایتون توضیح داده شده است

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

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

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

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

همگامی در مقابل موازی

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

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

در مقابل،

موازی گرایی در مورد به حداکثر رساندن استفاده از منابع سخت افزاری است. اگر هشت هسته CPU دارید، نمی‌خواهید تنها یکی را حداکثر کنید در حالی که هفت هسته دیگر بیکار هستند. در عوض، می‌خواهید در صورت امکان، فرآیندها یا رشته‌هایی را راه‌اندازی کنید که از همه آن هسته‌ها استفاده می‌کنند.

موازی و همزمانی در پایتون

پایتون مکانیسم‌هایی را برای همزمانی و موازی‌سازی ارائه می‌کند که هر کدام دارای نحو و موارد استفاده خاص خود هستند. برای همزمانی، پایتون دو مکانیسم مختلف را ارائه می دهد که بسیاری از مؤلفه های مشترک را به اشتراک می گذارند. اینها رشته و کوروتین یا ناهمگام هستند.

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

هر سه این مکانیسم – نخ‌سازی، کوروتین‌ها و پردازش چندگانه – موارد استفاده متفاوتی دارند. Threading و coroutines اغلب می توانند به جای هم استفاده شوند، اما نه همیشه. پردازش چندگانه قوی‌ترین مکانیسم است که برای سناریوهایی استفاده می‌شود که در آن نیاز به حداکثر استفاده از CPU دارید.

رشته کردن پایتون

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

5 ابزار برای کنترل چرخه عمر توسعه نرم افزار شما

در اینجا یک مثال ساده از threading در پایتون آمده است:


from concurrent.futures import ThreadPoolExecutor
import urllib.request as ur

datas = []

def get_from(url):
    connection = ur.urlopen(url)
    data = connection.read()
    datas.append(data)

urls = [
    "https://python.org",
    "https://docs.python.org/"
    "https://wikipedia.org",
    "https://imdb.com",    
]

with ThreadPoolExecutor() as ex:
    for url in urls:
        ex.submit(get_from, url)
       
# let's just look at the beginning of each data stream
# as this could be a lot of data
print ([_[:200] for _ in datas])

این قطعه از threading برای خواندن داده‌ها از چندین URL به طور همزمان، با استفاده از چندین نمونه اجرا شده از تابع get_from() استفاده می‌کند. سپس نتایج در یک لیست ذخیره می شود.

به‌جای ایجاد مستقیم رشته‌ها، این مثال از یکی از مکانیسم‌های راحت پایتون برای اجرای رشته‌ها استفاده می‌کند، ThreadPoolExecutor. ما می‌توانیم ده‌ها URL را به این روش بدون کاهش سرعت ارسال کنیم، زیرا هر رشته زمانی که فقط منتظر پاسخگویی یک سرور راه دور باشد، به دیگران تسلیم می‌شود.

کاربران پایتون اغلب در مورد اینکه آیا رشته‌های موجود در پایتون مشابه رشته‌هایی هستند که توسط سیستم عامل اصلی در معرض دید قرار می‌گیرند سردرگم می‌شوند. در CPython، پیاده‌سازی پیش‌فرض Python که در اکثریت قریب به اتفاق برنامه‌های Python استفاده می‌شود، رشته‌های Python رشته‌های سیستم‌عامل هستند — آنها فقط توسط زمان اجرا پایتون مدیریت می‌شوند تا به صورت مشترک اجرا شوند و در صورت نیاز به یکدیگر تسلیم شوند. /p>

مزایای رشته های پایتون

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

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

معایب رشته های پایتون

همانطور که قبلاً ذکر شد، موضوعات همکاری هستند. زمان اجرا پایتون توجه خود را بین آنها تقسیم می کند، به طوری که اشیاء قابل دسترسی توسط رشته ها می توانند به درستی مدیریت شوند. در نتیجه، نخ ها نباید برای کارهای فشرده CPU استفاده شوند. اگر یک عملیات فشرده CPU را در یک رشته اجرا کنید، زمانی که زمان اجرا به رشته دیگری تغییر می کند، متوقف می شود، بنابراین هیچ مزیتی نسبت به اجرای آن عملیات در خارج از یک رشته وجود نخواهد داشت.

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

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

کوروتین‌های پایتون و ناهمگام

Coroutines یا async روشی متفاوت برای اجرای همزمان توابع در پایتون، از طریق ساختارهای برنامه‌نویسی خاص به جای رشته‌های سیستمی است. کوروتین ها نیز توسط زمان اجرا پایتون مدیریت می شوند اما به سربار بسیار کمتری نسبت به رشته ها نیاز دارند.

Google API LLM ها را به دستگاه های اندروید و iOS می آورد

این هم نسخه دیگری از برنامه قبلی است که به عنوان یک ساختار async/coroutine و با استفاده از کتابخانه ای که از رسیدگی ناهمزمان درخواست های شبکه پشتیبانی می کند، نوشته شده است:


import aiohttp
import asyncio

urls = [
    "https://imdb.com",    
    "https://python.org",
    "https://docs.python.org",
    "https://wikipedia.org",
]

async def get_from(session, url):
    async with session.get(url) as r:
        return await r.text()


async def main():
    async with aiohttp.ClientSession() as session:
        datas = await asyncio.gather(*[get_from(session, u) for u in urls])
        print ([_[:200] for _ in datas])

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

get_from() یک coroutine است، به عنوان مثال، یک شی تابع که می تواند در کنار سایر کوروتین ها اجرا شود. asyncio.gather چندین برنامه را راه‌اندازی می‌کند (نمونه‌های متعددی از get_from() واکشی URL‌های مختلف)، منتظر می‌ماند تا همه آنها کامل شوند، سپس نتایج جمع‌آوری شده‌شان را به عنوان یک لیست برمی‌گرداند.< /p>

کتابخانه aiohttp اجازه می دهد تا اتصالات شبکه به صورت ناهمزمان برقرار شود. ما نمی‌توانیم از urllib.request ساده قدیمی در یک برنامه مشترک استفاده کنیم، زیرا پیشرفت سایر درخواست‌های ناهمزمان را مسدود می‌کند.

مزایای برنامه های پایتون

کوروتین‌ها در نحو برنامه کاملاً واضح هستند که توابع در کنار هم اجرا می‌شوند. با یک نگاه می توانید بفهمید که get_from() یک برنامه مشترک است. با thread ها، هر تابعی را می توان در یک رشته اجرا کرد، و استدلال در مورد آنچه ممکن است در یک رشته در حال اجرا باشد را دشوارتر می کند.

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

معایب برنامه های پایتون

Coroutineها و async نیاز به نوشتن کدی دارند که از نحو متمایز خود پیروی کند، استفاده از async def و await. چنین کدی با طراحی، نمی تواند با کدهای همزمان ترکیب شود. برای برنامه نویسانی که عادت ندارند به این فکر کنند که چگونه کدشان می تواند به صورت ناهمزمان اجرا شود، استفاده از کوروتین ها و ناهمگام یک منحنی یادگیری را ارائه می دهد.

همچنین، کوروتین‌ها و async به وظایف فشرده CPU اجازه نمی‌دهند که در کنار یکدیگر کارآمد اجرا شوند. مانند رشته ها، آنها برای عملیاتی طراحی شده اند که باید در شرایط خارجی منتظر بمانند.

چندپردازش پایتون

Multiprocessing به شما امکان می دهد تا با راه اندازی چندین نسخه مستقل از زمان اجرا پایتون، بسیاری از کارهای فشرده CPU را در کنار یکدیگر اجرا کنید. هر نمونه پایتون کد و داده های مورد نیاز برای اجرای وظیفه مورد نظر را دریافت می کند.

فهرست ۳ اسکریپت خواندن وب ما را برای استفاده از پردازش چندگانه بازنویسی می کند.


import urllib.request as ur
from multiprocessing import Pool
import re

urls = [
    "https://python.org",
    "https://docs.python.org",
    "https://wikipedia.org",
    "https://imdb.com",    
]

meta_match = re.compile("<meta .*?>")

def get_from(url):
    connection = ur.urlopen(url)
    data = str(connection.read())
    return meta_match.findall(data)

def main():
    with Pool() as p:
        datas = p.map(get_from, urls)
    print (datas)
# We're not truncating data here,
# since we're only getting extracts anyway
if __name__ == "__main__": main()

شئ Pool() نشان دهنده گروهی از فرآیندهای قابل استفاده مجدد است. .map() به شما امکان می دهد یک تابع برای اجرا در این فرآیندها و یک تابع تکرار شونده برای توزیع بین هر نمونه از تابع – در این مورد، get_from و لیستی از تابع ارسال کنید. URL ها.

جاوا 22 پیشرفت های امنیتی را به ارمغان می آورد

یک تفاوت کلیدی دیگر در این نسخه از اسکریپت این است که ما یک عملیات محدود به CPU را در get_from() انجام می دهیم. عبارت منظم هر چیزی را که شبیه تگ meta باشد جستجو می کند. البته این روش ایده‌آلی برای جستجوی چنین چیزهایی نیست، اما نکته اینجاست که می‌توانیم عملیاتی را که می‌تواند از نظر محاسباتی گران‌قیمت باشد در get_from انجام دهیم، بدون اینکه تمام درخواست‌های دیگر را مسدود کنیم.< /p>

مزایای چند پردازش پایتون

با threading و coroutines، زمان اجرا پایتون همه عملیات را مجبور می‌کند به صورت سریال اجرا شوند، بهتر است دسترسی به هر شی پایتون را مدیریت کنید. پردازش چندگانه با دادن یک زمان اجرا مجزای پایتون و یک هسته کامل CPU به هر عملیات از این محدودیت کنار می‌زند.

معایب چند پردازش پایتون

چندپردازش دو جنبه منفی دارد. اول، سربار اضافی با ایجاد فرآیندها مرتبط است. با این حال، اگر این فرآیندها را یک بار در طول عمر یک برنامه بچرخانید و دوباره از آنها استفاده کنید، می توانید تأثیر این را به حداقل برسانید. شی Pool در لیست ۳ می تواند به این صورت عمل کند: پس از راه اندازی، می توانیم کارهایی را در صورت نیاز به آن ارسال کنیم، بنابراین فقط یک بار هزینه در طول عمر برنامه برای شروع فرآیندهای فرعی وجود دارد.

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

از کدام مدل همزمانی پایتون استفاده کنم؟

هر زمان که عملیات طولانی مدت و فشرده CPU را انجام می دهید، از چند پردازش استفاده کنید. «CPU-intensive» به کارهایی اشاره دارد که در زمان اجرا پایتون انجام می‌شود (به عنوان مثال، عبارات منظم در فهرست ۳). شما نمی خواهید زمان اجرا پایتون به یک نمونه محدود شود که هنگام انجام کار مبتنی بر CPU مسدود شود.

برای عملیاتی که CPU را درگیر نمی‌کند، اما نیاز به انتظار در یک منبع خارجی دارد، مانند تماس شبکه، از threading یا coroutines استفاده کنید. در حالی که تفاوت در کارایی بین این دو هنگام انجام تنها با چند کار به طور همزمان ناچیز است، کوروتین ها هنگام برخورد با هزاران کار کارآمدتر خواهند بود، زیرا مدیریت تعداد زیادی از کارها برای زمان اجرا آسان تر از تعداد زیادی رشته است.

در نهایت، توجه داشته باشید که برنامه‌های مشترک هنگام استفاده از کتابخانه‌هایی که خود ناهمگام هستند، مانند aiohttp در فهرست ۲، بهترین کار را دارند. اگر برنامه‌های مشترک شما غیرهمگام‌ساز نباشند، می‌توانند پیشرفت سایر موارد را متوقف کنند. کوروتین ها.