در دنیای نرمافزار، بهینهسازی زمان پاسخگویی، بهرهوری بهتر از منابع سختافزاری و افزایش عملکرد برنامهها اهمیت ویژهای دارد. استفاده از مفاهیم همزمانی (Concurrency) و Parallelism به همراه Threading یکی از راهکارهای اساسی برای رسیدن به این اهداف محسوب میشود. در این بخش، به بررسی دقیق این مفاهیم، تفاوتهای آنها، مزایا و چالشها و همچنین کاربردهای عملی در زبانهایی مانند پایتون خواهیم پرداخت.
تعریف همزمانی (Concurrency)
همزمانی به معنای مدیریت و سازماندهی چندین فعالیت یا وظیفه بهگونهای است که به نظر میرسد همزمان در حال اجرا هستند؛ اما در واقع، زمانبندیهای متناوب بین این وظایف صورت میگیرد. برای مثال، هنگامی که یک برنامه در حال انتظار دریافت داده از یک منبع ورودی (مانند شبکه یا دیسک) است، میتواند از فرصت استفاده کرده و به فعالیتهای دیگری بپردازد. این روش باعث میشود تا زمان بیکاری کاهش یابد و کارایی کلی برنامه افزایش یابد.
تعریف Parallelism
توازی به معنای اجرای همزمان چندین کار بهطور واقعی و در لحظهای واحد است. برخلاف همزمانی که فعالیتها در قالب نوبتی انجام میشوند، در پردازش موازی چندین وظیفه بهصورت عینی روی چندین هسته یا پردازنده اجرا میشود. این رویکرد بهویژه در مواقعی که نیاز به پردازشهای محاسباتی سنگین است مفید واقع میشود؛ زیرا امکان تقسیمبندی کارها بین چندین واحد پردازش، زمان کلی اجرای الگوریتمهای پیچیده را بهشدت کاهش میدهد.
تعریف Threading
Threads واحدهای اجرایی کوچکتری در درون یک فرایند هستند که به برنامه اجازه میدهند چند عملیات را بهطور همزمان انجام دهد. در بسیاری از زبانهای برنامهنویسی مانند پایتون، جاوا، سیپلاسپلاس و غیره از Threads برای ایجاد ساختارهای همزمان و مدیریت چندوظیفهای استفاده میشود. در پایتون، ماژول threading ابزار اصلی برای اجرای Thread است. اما باید توجه داشت که به علت وجود Global Interpreter Lock (GIL) در پایتون، Threading برای کارهای محاسباتی سنگین (CPU-bound) محدودیتهایی دارند؛ از سوی دیگر برای کارهای I/O-bound میتوانند بسیار مفید باشند.
تفاوتها و شباهتها
همزمانی (Concurrency):
مدیریت چند وظیفه به صورت نوبتی.
مناسب برای کارهایی که انتظار دریافت داده (I/O-bound) دارند.
امکان بهبود پاسخگویی و بهرهوری از زمانهای بیکاری.
Parallelism:
اجرای عینی چند کار بهطور همزمان بر روی چندین هسته یا پردازنده.
مناسب برای وظایف محاسباتی سنگین (CPU-bound) که میتوان آنها را به بخشهای کوچک تقسیم کرد.
نیاز به هماهنگی دقیق بین واحدهای اجرایی برای جلوگیری از اشتباهات احتمالی.
Threading:
ایجاد چندین Thread در یک فرایند به منظور رسیدن به همزمانی.
در اغلب موارد برای کارهای I/O-bound مناسب است.
چالشهایی مانند هماهنگی بین Thread و رعایت اصول همگامسازی وجود دارد.
مزایا و چالشهای استفاده
مزایا
بهبود کارایی: استفاده از همزمانی و Parallelism باعث میشود تا زمانهای انتظار کاهش یابد و قسمتهای مختلف برنامه از توان پردازشی بهینهتری بهرهمند شوند.
افزایش پاسخگویی: برنامههای همزمان قادرند به درخواستهای کاربر سریعتر پاسخ دهند، بخصوص در برنامههای شبکهای یا آنهایی که نیاز به دسترسی به منابع خارجی دارند.
بهینهسازی منابع: تقسیمبندی کارها و اجرای موازی آنها اجازه میدهد که منابع سختافزاری مانند CPU و حافظه به صورت بهینهتری استفاده شوند.
چالشها
هماهنگی و همگامسازی: بهروزرسانی اطلاعات مشترک بین Thread نیازمند استفاده از قفلها (Locks) و سایر تکنیکهای همگامسازی (Synchronization) است تا از بروز خطاهای رقابتی جلوگیری شود.
پیچیدگی طراحی: طراحی سیستمهای همزمان و موازی نیاز به دانش عمیق از رفتار برنامه و مدیریت شرایط ویژه (مانند وضعیت رقابتی، بنبست و غیره) دارد.
محدودیتهای پایتون (GIL): در پایتون، به دلیل GIL تنها یک Thread در هر زمان قادر به اجرای کد پایتون است؛ این امر باعث میشود که برای کارهای محاسباتی سنگین باید از راهکارهای جایگزین مانند ماژول multiprocessing استفاده کرد.
کاربرد در پایتون
پایتون ابزارها و کتابخانههای متعددی برای بهرهگیری از همزمانی و توازی فراهم کرده است. مهمترین آنها عبارتند از:
۱. ماژول threading
این ماژول برای ایجاد نخهای سبک در برنامههای I/O-bound بسیار مناسب است. با استفاده از threading میتوان چندین کار همزمان به طور نوبتی اجرا نمود:
import threading
import time
def work():
for i in range(5):
print("thread is working…!", threading.current_thread().name)
time.sleep(1)
#
t1 = threading.Thread(target= work, name="thread-1")
t2 = threading.Thread(target= work, name="thread-2")
#
t1.start()
t2.start()
#
t1.join()
t2.join()
۲. ماژول multiprocessing
جهت بهرهمندی از توازی واقعی و استفاده از چند هسته پردازنده، میتوان از ماژول multiprocessing استفاده کرد. این ماژول به ایجاد چندین فرایند مستقل پرداخته که هر کدام دارای مفسر جداگانه پایتون هستند:
import multiprocessing
import time
def fun():
for i in range(5):
print("function is worked", multiprocessing.current_process().name)
time.sleep(1)
if __name__ == "__main__":
fun1 = multiprocessing.Process(target=fun, name="fun1")
fun2 = multiprocessing.Process(target=fun, name="fun2")
fun1.start()
fun2.start()
fun1.join()
fun2.join()
۳. ماژول asyncio
برای برنامههایی که به مدیریت تعداد زیادی درخواست ورودی/خروجی نیاز دارند، استفاده از الگوی async/await و کتابخانه asyncio توصیه میشود:
import asyncio
import random
async def func(number):
print(f"start func {number}")
t = random.randint(۱, ۴)
await asyncio.sleep(t)
print(f" finish func {number}")
async def main():
tasks = [asyncio.create_task(func(i)) for i in range(۱, ۸)]
await asyncio.gather(*tasks)
asyncio.run(main())
این مدل غیرهمزمان (Asynchronous) برای برنامههای شبکهای یا هر کاربردی که زمانبندیهای دقیق I/O اهمیت دارد بسیار مناسب است.
مقایسه با زبانهای دیگر
علاوه بر پایتون، زبانهای مختلفی نیز از مفاهیم همزمانی و توازی پشتیبانی میکنند:
جاوا: جاوا دارای کتابخانههای قوی مانند java.util.concurrent است که امکان مدیریت Threads، تسکها و هماهنگسازی دادهها را فراهم میکنند.
C++: استانداردهای مدرن C++ (از جمله C++11 به بعد) امکانات پیشرفتهای در زمینه نخسازی و پردازش موازی ارائه میدهند.
Go: زبان Go به واسطه گوروتینها (Goroutines) و کانالها (Channels) یکی از زبانهای مدرن و ساده برای برنامهنویسی همزمان و موازی محسوب میشود.
C#: پلتفرم .NET از طریق Task Parallel Library (TPL) امکانات کامل برای مدیریت عملیات همزمان و موازی فراهم ساخته است.
هر زبان بسته به معماری و نیازهای پروژه ابزار و تکنیکهای خاص خود را داراست که در صورت آشنایی عمیق از آنها میتوان به بهینهترین راه حل برای اجرای چند وظیفهای دست یافت.
نتیجهگیری
استفاده از همزمانی، Parallelism و Threading در برنامهنویسی ابزارهایی قدرتمند برای بهینهسازی عملکرد و بهرهوری از منابع سیستم هستند. با وجود چالشهایی مانند مدیریت همگامسازی و محدودیتهای معماری (مانند GIL در پایتون)، انتخاب صحیح روش و ابزار مناسب (مثلاً استفاده از multiprocessing در مقابل threading برای کارهای CPU-bound) میتواند تفاوت چشمگیری در کارایی و پاسخگویی نرمافزار ایجاد کند. برای توسعهدهندگان، درک عمیق این مفاهیم و انتخاب اشتراکات صحیح در طراحی سیستمهای چندوظیفهای نقش بسزایی در کیفیت و عملکرد نهایی برنامه دارد.
- Concurrency،Parallelism و Threading - خرداد ۸, ۱۴۰۴
- Object-relational Mappers - بهمن ۲۴, ۱۴۰۳
- مروری بر Quart در پایتون - بهمن ۲, ۱۴۰۳