Async، concurrency و parallelism یکی نیستند
· 5 دقیقه مطالعهمهندسی توسعه نرمافزار #csharp #dotnet #async #concurrency #parallelism
این سه واژه را خیلی وقتها به جای هم به کار میبریم، اما یک معنی ندارند.
ممکن است هر سه در یک برنامه، حتی در یک قابلیت، دیده شوند. با این حال هرکدام به پرسش متفاوتی جواب میدهند:
- Async: آیا این عملیات میتواند هنگام انتظار، مسیر اجرا را مسدود نکند؟
- Concurrency: آیا چند عملیات میتوانند در یک بازهی زمانی پیش بروند؟
- Parallelism: آیا چند محاسبهی CPU میتوانند همزمان روی هستههای متفاوت اجرا شوند؟
وقتی این پرسشها را جدا نگه داریم، انتخاب طراحی سادهتر میشود.
async دربارهی انتظار است
async وقتی مفید است که یک عملیات برای I/O مثل پاسخ HTTP، کوئری دیتابیس، خواندن فایل یا پیام سرویس دیگر منتظر میماند.
ممکن است عملیات زمان ببرد، اما در بخش بزرگی از این مدت CPU مشغول انجام آن نیست. async عملیات را شروع میکند، تا پاسخ سیستم بیرونی کنترل را واگذار میکند و اجازه میدهد کارهای آمادهی دیگر بهجای مسدود ماندن، ادامه پیدا کنند.[1]
دم کردن قهوه مثالی آشناست. وقتی کتری روی گاز است، ایستادن کنارش کمکی نمیکند. میتوانید ماگ را آماده کنید یا به یک پیام جواب بدهید و وقتی آب جوش آمد، برگردید. async هم همین کار را میکند: I/O را شروع میکند، هنگام انتظار کنترل را واگذار میکند و وقتی کاری برای انجام دادن هست، ادامه میدهد.
async سرویس راه دور را سریعتر نمیکند و خودبهخود هم thread تازهای نمیسازد. وظیفهی اصلیاش این است که در زمان انتظار بیرونی، ظرفیت اجرا را بیدلیل اشغال نکند.
concurrency دربارهی همپوشانی است
concurrency یعنی در یک بازهی زمانی، بیش از یک عملیات در حال پیش رفتن باشد. لازم نیست این عملیات دقیقاً در یک لحظه اجرا شوند.
فرض کنید میخواهید صبحانه آماده کنید. قهوه را دم میکنید و در زمان انتظار همزمان تخممرغ را آبپز میکنید یا کار دیگری را انجام میدهید؛ هیچکدام در تمام مدت به توجه شما نیاز ندارند، پس زمانهای انتظارشان روی هم میافتد. concurrency یعنی چند کار را در جریان نگه دارید، بدون اینکه هر انتظار را زمان بیهوده فرض کنید.
دو فایل مستقل را در نظر بگیرید. هر دو دانلود را شروع میکنید و هرکدام بخش زیادی از زمان دانلودش را منتظر شبکه میماند. وقتی فایل A منتظر داده است، فایل B ممکن است یک بستهی داده دریافت کند. کارشان با هم همپوشانی دارد، حتی اگر یک منبع اجرایی آنها را هماهنگ کند.
| عملیات | ۰ میلیثانیه | ۱ تا ۵۰۰ میلیثانیه | ۵۰۰ میلیثانیه |
|---|---|---|---|
| دانلود A | شروع | منتظر دادهی شبکه | کامل |
| دانلود B | شروع | منتظر دادهی شبکه | کامل |
این نمونهای از I/O همزمان است. چون زمانهای انتظار شبکه روی هم میافتند، ممکن است زمان کلی انتظار کاربر برای هر دو دانلود کمتر شود، اما این هنوز ثابت نمیکند که دو محاسبهی سنگین CPU دقیقاً همزمان اجرا شدهاند.
concurrency به هماهنگی نیاز دارد. ممکن است عملیات برای اتصال دیتابیس، محدودیت نرخ، فایل مشترک یا مقدار محدودی حافظه رقابت کنند. شروع کردن همهی عملیات در همان لحظه، همیشه انتخاب بهتری نیست.
parallelism دربارهی کار CPU است
parallelism فرق دارد. محاسبه را بین چند هستهی CPU تقسیم میکند تا محاسبات بتوانند همزمان اجرا شوند.
برای قهوه، دو نفر را تصور کنید که همزمان کار میکنند: یکی دانهها را آسیاب میکند و دیگری شیر را گرم میکند. هر دو واقعاً مشغول کارند، پس قهوه زودتر آماده میشود. parallelism هم همین است: کارهای مستقل و CPU-bound همزمان روی هستههای جدا اجرا میشوند.
دو تصویر بزرگ را تصور کنید که باید اندازهشان تغییر کند، فیلتر بخورند و histogram آنها محاسبه شود. این کار CPU-bound است. اگر دستگاه هستههای آزاد داشته باشد و دو تصویر مستقل باشند، پردازش موازی آنها میتواند زمان کل را کمتر کند. راهنمای parallel programming در .NET این کار را توزیع کار بین چند پردازنده میداند که میتوانند threadها را همزمان اجرا کنند.[3]
parallelism هزینه هم دارد. تقسیم کار، هماهنگ کردن نتایج و رقابت بر سر حافظه یا CPU cache ممکن است یک کار کوچک را کندتر کند. وقتی اندازهگیری نشان میدهد CPU گلوگاه است، ارزش بررسی دارد، نه صرفاً چون یک متد سنگین به نظر میرسد.
یک راهنمای کوتاه برای انتخاب
| وضعیت | انتخاب اصلی | دلیل |
|---|---|---|
| انتظار برای API، دیتابیس، فایل یا queue | I/O غیرهمگام | عملیات منتظر یک سیستم بیرونی است. |
| انتظار برای چند API مستقل | async همراه با concurrency | زمانهای انتظار میتوانند با هم همپوشانی داشته باشند. |
| تغییر دادن تعداد زیادی تصویر بزرگ یا محاسبات گران | parallelism | چند هستهی CPU میتوانند زمان محاسبه را کمتر کنند. |
| اعتبارسنجی یک دادهی کوچک یا قالببندی متن | کد synchronous | نه انتظار بیرونی معناداری وجود دارد و نه کار سنگین CPU. |
راهنمای async مایکروسافت هم همین تمایز عملی را مطرح میکند: async معمولاً انتخاب مناسب برای سناریوهای I/O-bound است، اما محاسبات گران، CPU-bound هستند و ممکن است به رویکرد دیگری نیاز داشته باشند.[2]