برنامهنویسی غیرهمگام: پیش از انتخاب ابزار، نوع انتظار را بشناسید
اگر برنامه برای پاسخ HTTP، نتیجهی پایگاه داده، خواندن فایل یا پایان timer منتظر بماند، روش غیرهمگام کمک میکند در این فاصله کار دیگری انجام دهد. البته زمان آمادهشدن پاسخ را کم نمیکند.
همزمانی یعنی چند عملیات مستقل در بازههای زمانی همپوشان پیش بروند. اگر این عملیاتها منتظر I/O باشند، همپوشانیشان میتواند زمان انتظار کلی را کم کند؛ اما باید تعدادشان را محدود کرد. اجرای موازی، کارهای وابسته به پردازنده را میان چند هسته تقسیم میکند. این روش وقتی میارزد که اندازهگیری، بهبودش را نشان دهد و هزینهی هماهنگسازی را توجیه کند.
در .NET، Task نشاندهندهی کاملشدن عملیات است و میتواند نتیجه، خطا یا لغو آن را هم در خود داشته باشد؛ اما خودش رشتهی اجرا نیست. با await، اگر Task هنوز تمام نشده باشد، متد موقتاً کنترل را واگذار میکند و پس از پایان کار از همانجا ادامه میدهد. در این فاصله، برنامه میتواند سراغ کار دیگری برود. در مقابل، مسدود کردن رشتهی اجرا باعث میشود تا پایان انتظار اشغال بماند. محل ادامهی متد ممکن است به کانتکست اجرا وابسته باشد. در کد کتابخانهای، ConfigureAwait(false) میگوید ادامهی متد لازم نیست به همان کانتکست برگردد؛ این انتخاب به نیاز کد بستگی دارد و قاعدهای همگانی نیست.
- چرا برنامهنویسی async وجود دارد؟
بخش زیادی از کار برنامه، منتظر ماندن برای شبکه، دیتابیس یا فایل است؛ کارهایی که زمان تمام شدنشان دست برنامه نیست. async نمیگذارد این انتظار، رابط کاربری یا رسیدگی به درخواستهای دیگر را متوقف کند. تأخیر شبکه یا دیتابیس با async ناپدید نمیشود. تفاوت در این است که برنامه هنگام انتظار بیکار نمیماند و میتواند سراغ کاری برود که همین حالا آمادهی انجام است.
- Async، concurrency و parallelism یکی نیستند
async، concurrency و parallelism شبیه هم به نظر میرسند، اما به سه مسئلهی متفاوت جواب میدهند. اولی دربارهی انتظار است، دومی دربارهی پیش بردن چند کار، و سومی دربارهی تقسیم محاسبه میان هستهها. وقتی این تفاوت را روشن کنیم، انتخاب سادهتر میشود. برای انتظار I/O سراغ async میرویم؛ برای محاسبهی سنگین CPU فقط وقتی parallelism لازم است که واقعاً اندازهگیری گلوگاه را نشان دهد.
- Task، async و await: مسیر کار غیرهمگام از شروع تا نتیجه
مسیر متدی را دنبال میکنیم که به پاسخ HTTP نیاز دارد: درخواست را شروع میکند، متن پاسخ را میگیرد و آن را به نتیجه تبدیل میکند. تسک درخواست HTTP و تسک خود متد، هرکدام بخش متفاوتی از این کار را نشان میدهند. میبینیم await هنگام انتظار چه میکند، خطا و لغو چگونه به فراخواننده میرسند و متد پس از آماده شدن پاسخ کجا ادامه پیدا میکند.