Multithreading در پایتون
Multithreading در پایتون
در این مقاله با اصول Multithreading در زبان برنامه نویسی پایتون آشنا خواهید شد. چند رشته ای یا Multithreading در پایتون به شما امکان میدهد تا چندین کد موازی را در یک برنامه اجرا کنید، به طوری که همه آنها به صورت همزمان اجرا شوند. این امکان به شما کمک میکند تا عملیاتهای موازی را انجام دهید و بهرهوری بیشتری از پردازشهای چند مهمانه (Multithreading) ببرید.
thread چیست؟
Thread یک مفهوم مهم در برنامهنویسی چندمهاره (Multithreading) است که به شما امکان میدهد تا چندین فرآیند (کد) را به صورت همزمان و موازی در یک برنامه اجرا کنید. هر Thread یک نخ از اجرای برنامه را نمایان میکند و دارای دستورالعمل و حافظه مخصوص به خود است.
تا زمانی که یک برنامه تنها یک نخ (Main Thread) داشته باشد، اجرای کد به صورت ترتیبی انجام میشود. اما با استفاده از چندین Thread، میتوانید قسمتهای مختلفی از برنامه را به صورت همزمان اجرا کرده و عملیاتهای موازی را انجام دهید. این امکان بهبود کارایی برنامه و بهرهوری پردازشی را فراهم میکند.
در زبانهای برنامهنویسی مختلف، نحوه ایجاد و مدیریت Thread ممکن است متفاوت باشد. در پایتون، ما از ماژول threading برای ایجاد و مدیریت Threadها استفاده میکنیم.
برای ایجاد یک Thread در پایتون، معمولاً شما نیاز دارید تا یک تابع یا متد را به عنوان هدف (target) برای Thread مشخص کنید، سپس این Thread را شروع کنید (start) تا اجرای تابع موردنظر در یک نخ جدید آغاز شود.
هر فرآیندی دارای 3 جزء اساسی است:
- برنامه قابل اجرا
- داده های مورد نیاز برنامه مانند: متغییر ها، bufferها و غیره
- فرایند اجرای برنامه
فواید Multithreading
- پاسخگویی (Responsiveness) : یک رشته ممکن است پاسخ سریعی ارائه دهد در حالی که رشته های دیگر مسدود شده باشد یا به دلیل محاسبات زیاد کند شده باشد.
- به اشتراک گذاری منابع (Resource sharing) : بهطور پیشفرض thread ها کد، داده و سایر منابع مشترک را به اشتراک میگذارند، که اجازه میدهد چندین کار به طور همزمان در یک فضای واحد انجام شود.
- مقیاس پذیری (Scalability) : استفاده از معماری های Multithreading، یک thread تنها می تواند روی یک CPU اجرا شود، مهم نیست که چند پردازنده در دسترس باشد.
چالش های برنامه نویسی
برای برنامه نویسان، پنج حوزه وجود دارد که تراشه های چند هسته ای(multi-core) چالش های جدیدی را ارائه می دهد:
- شناسایی وظایف (Identifying tasks) : بررسی برنامه برای پیدا کردن فعالیت هایی که در یک زمان اجرا می شود.
- تعادل (Balance): یافتن وظایفی برای اجرای همزمان که ارزش برابری دارند. یعنی یک thread را برای کارهای بی اهمیت هدر ندهید.
- تقسیم بندی داده ها (Data splitting) : برای جلوگیری از تداخل thread ها با یکدیگر.
- وابستگی به داده ها (Data dependency) : اگر یک کار به نتایج کار دیگری وابسته است، باید کارها هماهنگ شوند تا دسترسی ها به ترتیب اجرا شوند.
- تست و رفع اشکال (Testing and debugging) : برنامه را اجرا کنید و مشکلات آن را حل کنید، باید توجه داشته باشید تا thread ها به ترتیب اجرا شوند.
Multithreading در پایتون
در پایتون از کتابخانه threading برای ایجاد thread استفاده میکنیم. بیایید با یک مثال ساده درک بهتری از این موضوع داشته باشیم.
- در ابتدا کتابخانه threading را فراخوانی میکنیم، تا امکان استفاده از متدها فراهم شود.
- در خط های ۳ تا ۷ فانکشن ها را تعریف میکنیم.
- دلیل استفاده از دستور if این است که تنها زمانی اجرا می شود که به عنوان یک اسکریپت اجرا کنید. به عنوان مثال: python3 threads.py
output kaliboys-2 kaliboys-1 Done!
- در خط های ۱۰ و ۱۱ متغییر هایی با کمک کتابخانه threading تعریف کرده و با متد Thread به آنها گفتیم که در ابتدا کدام تابع را اجرا کند. همانطور که میبینید با استفاده از target به برنامه میفهمانیم کدام تابع را اول اجرا کند.
- برای شروع یک thread از متد start کلاس Thread استفاده می کنیم.
- متد join به ما این کمک را میکند تا تا زمانی thread اولی به اتمام نرسد thread بعدی اجرا نمی شود. میتوان اینگونه هم بیان کرد.
- در نتیجه برنامه فعلی ابتدا منتظر تکمیل t1 و سپس t2 خواهد ماند. هنگامی که آنها به پایان برسد، دستورات باقی مانده از برنامه فعلی اجرا می شود.
Threading Objects در پایتون
Semaphore
اولین شی threading پایتون که باید به آن نگاه کرد threading.Semaphore است. Semaphore یک شمارنده با ویژگی های خاص است. اولین ویژگی آن، شمارش اتمی است. شمارش اتمی به این معنی است که سیستم عامل در وسط افزایش یا کاهش شمارنده thread را عوض نکند. Semaphoreها اغلب برای محافظت از منابعی که ظرفیت محدودی دارند استفاده می شوند. به عنوان مثال اگر شما یک مجموعه از اتصالات دارید و می خواهید اندازه اتصالات را به تعداد خاصی محدود کنید.
Timer
Threading.Timer راهی برای تنظیم کردن یک تابع است که پس از گذشت مدت زمان مشخصی فراخوانی شود. شما یک تایمر را می توانید به صورت زیر طراحی کنید:
t = threading.Timer(30.0, my_function)
شما تایمر را با فراخوانی start شروع می کنید. این تابع در مقطعی پس از زمان مشخص شده در یک رشته جدید فراخوانی می شود، اما توجه داشته باشید که هیچ تضمینی وجود ندارد که دقیقاً در زمان مورد نظر شما فراخوانی شود.
Barrier
Threading.Barrier می تواند برای همراه نگه داشتن تعداد ثابتی از رشته ها استفاده شود. هنگام ایجاد یک مانع، تماس گیرنده باید مشخص کند که چه تعداد رشته باهم همراه می شوند. هر رشته .wait() را در Barrier فراخوانی می کند. همه آنها تا زمانی که تعداد مشخصی از رشته ها منتظر بمانند مسدود می شوند و سپس همه همزمان آزاد می شوند.
بیشتر بخوانید: