📌برسی تفاوت Task و ValueTask
زمانیکه تصمیم میگیریم کدهای زده شده را بهینه کنیم، اکثرا دنبال راه حلهای جدید نمیگردیم. این مورد کاملا غریزی است؛ چرا که بهدنبال کمترین انرژی و بیشترین بازدهی هستیم؛ این طبیعت انسان است. صرفا کدهای قبلی را بازبینی میکنیم و سعی میکنیم نحوهی نوشتن منطقهای موجود را بهینه کنیم. در همین راستا درک عملکرد Task و ValueTask ها شاید قدمی مهم در مورد بهینه کردن کدها باشد؛ چرا استفاده درست و بجای این دو مورد میتواند تاثیر زیادی بر روی سرعت و استفاده از مصرف حافظه داشته باشد؟ در این مقاله سعی میکنیم تا درک درستی از این دو داشته باشیم.
✨️ء<Task<T چیست؟
ءTask یک کلاس در فضای نام System.Threading.Tasks است؛ بهطوریکه کمک میکند تا یک قسمت از برنامه به صورت مستقل از Thread اصلی اجرا شود. بهبیان دیگر میتواند یک Thread Pool را ایجاد و با توجه به روند کار، از یک مرحلهی اجرایی به مرحلهای دیگر منتقل میکند. همچنین هر Task میتواند یک مقدار برگشتی نیز داشته باشد.
این درحالیاست که میتواند صرفا یک فرآیند را اجرا کند، بدون اینکه خروجی داشته باشد. بهعبارتی دیگر اگر فرآیندی داشته باشیم که در نهایت یک شناسه را برمیگرداند، از <Task<int و اگر فرآیندی داشته باشیم که صرفا فرآیند همگام سازی دادههای قدیمی به جدید را انجام میدهد، میتواند از نوع Task باشد.
همانطور که اشاره شد، Task یک کلاس است که شامل متدها و فیلدهای مختلفی میباشد. با استفاده از این اعضا میتوان نحوهی اجرای کدها و وضعیتهای مختلف اجرای آن را مدیریت کرد، تا در نهایت اجرای آن کامل شود.
به دلیل اینکه Task یک class است و class ها از نوع ReferenceType میباشند، روی حافظهی Heap ذخیره میشوند و بهازای هر بار فراخوانی متدی که خروجی Task دارد، شیء Task را روی Heap ذخیره میکند. این شیء وضعیت اجرای قسمتی از کد ما را که میتواند sync یا async باشد، در خود ذخیره میکند تا در نهایت اجرای آن کامل شود.
💡نحوه استفاده از <Task<T
برای درک بهتر، یک تکه کد را با بهره بردن از Task ایجاد میکنیم :
public static class DummyWeatherProvider
{
public static async Task<Weather> Get(string city)
{
await Task.Delay(10);
var weather = new Weather
{
City = city,
Date = DateTime.Now,
AvgTempratureF = new Random().Next(5, 70)
};
return weather;
}
}
همان طور که مشخص است، کلاس موجود یک متد به نام Get دارد تا اطلاعات آب و هوای شهر مورد نظر را به صورت یک Task برگرداند. حال کد زیر را جهت بررسی تغییر وضعیتهای اجرایی این Task ایجاد میکنیم :
static async Task CheckTaskStatus()
{
var task = DummyWeatherProvider.Get("Stockholm");
LogTaskStatus(task.Status);
await task;
LogTaskStatus(task.Status);
}
static void LogTaskStatus(TaskStatus status)
{
Console.WriteLine($"Task Status: {Enum.GetName(typeof(TaskStatus), status)}");
}
ءTaskStatus یک enumeration است، بهطوریکه بیانگر وضعیتهای مختلف یک Task در حال اجرا میباشد. برای مثال: WaitingForActivation, Running, RanToCompletion. در کد بالا ابتدا متد را فراخوانی میکنیم. سپس منتظر میمانیم تا متد اجرا شده، تکمیل شود. در اولین لاگ وضعیت، به WaitingForActivation و در دومین لاگ به RanToCompletion تبدیل میشود. حالکه با Task ها و نحوهی اجرای فرآیند آن آشنا شدیم، در قسمت بعدی به بررسی ValueTask ها میپردازیم.
✨️ء<ValueTask<T چیست؟
همانند تسک ها ، ValueTask هم برای مدیریت وضعیت فرآیند استفاده میشود؛ با این تفاوت که ValueTask ها از نوع struct هستند. بهطوریکه نحوهی ذخیره سازی آنها در حافظه به نسبت class ها کاملا متفاوت است. از نقطه نظر سرعت، تشخیص دادن اینکه کدامیک باید استفاده شود، باید با توجه به سناریو، بررسی و انتخاب شود؛ چرا که از نظر تخصیص حافظه متفاوت عمل میکنند. برای درک بهتر عملکرد ValueTask ها کد زیر را بررسی میکنیم :