Використання класів Завдання або завдань має вузьке місце продуктивності, про яке ми не згадували в попередніх статтях. Коротко кажучи, ці класи лідирують, коли результати одразу доступніНепотрібне розподілення。 Це означає, що новий об'єкт Завдання або Завдання завжди буде створений, навіть якщо результат уже доступний. Ми згадували, що концепція async/await, яку ми використовували в попередніх статтях, існує ще з релізу .NET 4.5. Ця функція була вдосконалена з версії .NET 4.7 із структурою ValueTask, яка може використовуватися як повернення для асинхронних функцій.
Структура ValueTask
Структура ValueTask вперше з'явилася в репозиторії corefxlab у 2015 році. Цей репозиторій використовується для експериментів і дослідження нових ідей, які можуть потрапити або не потрапити до основного репозиторію corefx. Репозиторій Corefx — це репозиторій, де розташовані всі базові бібліотеки .NET Core. Вона була розроблена та запропонована Стівеном Таубом для бібліотеки System.Threading.Tasks.Channels. Тоді Стівен коротко пояснив:
Адреса бібліотеки corefxlab:Вхід за гіперпосиланням видно.
ValueTask — це окреме об'єднання T і Завдання, що дозволяє ReadAsync вільно виділяти для синхронного повернення доступних T значень (на відміну від використання Task.FromResult, який вимагає виділення екземпляра Завдання). ValueTask є очікуваним, тому споживання більшості екземплярів не відрізняється від споживання завдань. Багато хто бачить переваги використання цієї структури, яка включена до C# 7 як частина пакету System.Threading.Tasks.Extensions NuGet. Отже, перш ніж ми зануримося у структуру ValueTask, розглянемо проблему, яку вона використовує для розв'язання. Оскільки Task(Task) є типом посилання, починайте зАсинхронний метод, що повертає об'єкт завдання, означає, що він виділяється на купу щоразу。 Це необхідно у багатьох випадках.
Однак у деяких випадках асинхронні методи повертають результати одразу або завершують синхронно. У таких випадках таке виділення є зайвим і може стати дорогим у критично важливих для продуктивності частинах коду. До релізу .NET 4.7 уникнути цього не було, оскільки асинхронні методи мали повертати Завдання, <T>Завдання або void (останній зазвичай був небажаним). У цій версії .NET це розширено, тобто асинхронний метод може повертати будь-який тип, якщо має доступний метод GetAwaiter. ValueTask є конкретним прикладом цього типу, і його також додали до цього релізу.
Ви можете переглянути репозиторій corefx і побачити повну реалізацію ValueTask, ось розділ API, який нас цікавить:
Як структура, ValueTask дозволяє писати асинхронні методи, які не виділяють пам'ять під час синхронного виконання. Узгодженість API концепції async/await не порушується таким чином. Крім того, ця конструкція працює самостійно, що робить її зручною у використанні. Наприклад, якщо виконати цей простий код:
У методі MultiplyAsync ми імітуємо ситуацію, коли хочемо уникнути використання Завдання і повернути лише просте ціле число. Це робиться у операторі if методу, де ми фактично перевіряємо, чи дорівнює переданий параметр нуль. Проблема в тому, щоНавіть якщо наша умова в операторі if істинна, наведений вище код створює об'єкт Завдання。 Ми розв'язуємо цю проблему так:
ValueTask і Task
Як уже згадувалося, існує дві основні переваги використання ValueTask:
- Покращення продуктивності
- Збільшення гнучкості впровадження
Отже, які цифри стоять за покращеннями продуктивності? Дотримуйтесь цього коду:
Якщо ми запустимо цей код, на виконання JIT потрібно 120 нс. Тепер, якщо замінити Завдання на ValueTask ось так:
З JIT ми отримаємо час виконання 65 нс. Звісно, через Task.Delay ми не виконуємо синхронно, але бачимо покращення часу виконання.
Ще одна перевага, яку ми згадували, — це підвищена гнучкість у впровадженнях. Що ж це означає? Реалізації асинхронних інтерфейсів, які мають бути синхронізовані, будуть змушені використовувати Task.Run або Task.FromResult. Звісно, це призводить до проблем із продуктивністю, які ми обговорювали раніше. Коли ми використовуємо ValueTask, ми частіше обираємо між синхронними або асинхронними реалізаціями. Майте на увазі, що це може свідчити про те, що ваш код може бути неправильно спроєктований, якщо це трапиться з вами.
Наприклад, зверніть увагу на цей інтерфейс:
Припустимо, ви хочете назвати це з коду так:
Оскільки ми використовуємо ValueTask в інтерфейсі, реалізація інтерфейсу може бути синхронною або асинхронною. Ми можемо отримати цю перевагу, фактично пропускаючи додавання деяких функцій до IThing, які обробляють поведінку синхронізації. Таким чином набагато простіше використовувати цей інтерфейс. Ось синхронна реалізація наведеного інтерфейсу:
Ось асинхронна реалізація того ж інтерфейсу:
Однак перед використанням ValueTask слід врахувати деякі компроміси. Легко подумати, що за замовчуванням слід використовувати ValueTask замість Task, але це зовсім не так. Наприклад, хоча ValueTask допомагає уникнути зайвих призначень при доступній синхронізації результатів, він також містить два поля.
Важливо пам'ятати, що саме таку структуру ми використовуємо тут, тобто використовуємо типи значень і всі їхні тягарі. Завдання, навпаки, є типом референсу з лише одним полем.Коли ви використовуєте ValueTask, у нас є більше даних для обробки та обробки. Якщо такий метод очікується в асинхронному методі, то асинхронний метод має виглядАвтомат станів також буде більшим, оскільки зберігання всієї структури зазвичай потребує більше простору, ніж зберігання одного посилання.
Ось чому в Microsoft насправді рекомендують використовувати Task або Task як тип повернення за замовчуванням для асинхронних методів. Лише після аналізу продуктивності варто розглянути можливість переходу на ValueTask.
зведення
ValueTask — це структура, введена в .NET 4.7, яка дає нам багато можливостей для використання асинхронних методів у .NET. Однак це не позбавлено своєї ціни. Це корисно для критичних для продуктивності методів, які виконуються синхронно. З ними ми можемо уникнути призначення зайвих об'єктів. Проте, як тип цінності, він має всі проблеми, які зазвичай мають типи цінності. Тому ми можемо скористатися цією структурою, але маємо бути обережними.
Оригінальна адреса:Вхід за гіперпосиланням видно.
|