Кожного разу, коли вам потрібно виконати дію на віддаленому сервері, ваша програма генерує запит, надсилає його, а потім чекає відповіді. Я буду використовувати SaveChanges()
і в SaveChangesAsync()
якості прикладу , але те ж саме відноситься і до Find()
і FindAsync()
.
Скажімо, у вас є список myList
із понад 100 елементів, які потрібно додати до бази даних. Щоб вставити це, ваша функція виглядатиме приблизно так:
using(var context = new MyEDM())
{
context.MyTable.AddRange(myList);
context.SaveChanges();
}
Спочатку ви створюєте екземпляр MyEDM
, додаєте список myList
до таблиці MyTable
, а потім викликаєте, SaveChanges()
щоб зберегти зміни в базі даних. Це працює, як ви хочете, записи фіксуються, але ваша програма не може робити нічого іншого, поки фіксація не закінчиться. Це може зайняти багато часу, залежно від того, що ви робите. Якщо ви вносите зміни до записів, суб'єкт повинен фіксувати їх по одному (у мене колись було 2 хвилини збереження для оновлення)!
Щоб вирішити цю проблему, ви можете зробити одну з двох речей. Перший - ви можете запустити новий потік для обробки вставки. Поки це звільнить викличний потік для продовження виконання, ви створили новий потік, який просто буде сидіти там і чекати. У цих накладних витратах немає потреби, і це те, що async await
вирішує шаблон.
Що стосується операцій вводу-виводу, await
швидко стає вашим найкращим другом. Беручи розділ коду зверху, ми можемо змінити його так:
using(var context = new MyEDM())
{
Console.WriteLine("Save Starting");
context.MyTable.AddRange(myList);
await context.SaveChangesAsync();
Console.WriteLine("Save Complete");
}
Це дуже незначна зміна, але це суттєво впливає на ефективність та продуктивність вашого коду. То що трапляється? Початку коду є те ж саме, ви створюєте екземпляр MyEDM
і додати myList
To MyTable
. Але коли ви телефонуєте await context.SaveChangesAsync()
, виконання коду повертається до функції виклику! Отже, поки ви чекаєте на всі ці записи, щоб зафіксувати ваш код може продовжувати виконуватися. Скажімо, функція, що містила вищевказаний код, мала підпис public async Task SaveRecords(List<MyTable> saveList)
, виклична функція може виглядати так:
public async Task MyCallingFunction()
{
Console.WriteLine("Function Starting");
Task saveTask = SaveRecords(GenerateNewRecords());
for(int i = 0; i < 1000; i++){
Console.WriteLine("Continuing to execute!");
}
await saveTask;
Console.Log("Function Complete");
}
Чому у вас така функція, я не знаю, але те, що вона видає, показує, як це async await
працює. Спочатку давайте розглянемо те, що відбувається.
Виконання входить MyCallingFunction
, Function Starting
потім Save Starting
записується на консоль, потім SaveChangesAsync()
викликається функція . На цьому етапі виконання повертається MyCallingFunction
та вводить цикл написання циклу «Продовжуючи виконувати» до 1000 разів. Після SaveChangesAsync()
завершення виконання повертається до SaveRecords
функції, записуючи Save Complete
на консоль. Після того, як все SaveRecords
завершиться, виконання буде продовжено MyCallingFunction
правильно, якби це було після SaveChangesAsync()
закінчення. Розгублений? Ось приклад виводу:
Запуск функції
Зберегти Починаючи
Продовжуємо виконувати!
Продовжуємо виконувати!
Продовжуємо виконувати!
Продовжуємо виконувати!
Продовжуємо виконувати!
....
Продовжуємо виконувати!
Зберегти завершено!
Продовжуємо виконувати!
Продовжуємо виконувати!
Продовжуємо виконувати!
....
Продовжуємо виконувати!
Функція виконана!
Або можливо:
Запуск функції
Зберегти Починаючи
Продовжуємо виконувати!
Продовжуємо виконувати!
Зберегти завершено!
Продовжуємо виконувати!
Продовжуємо виконувати!
Продовжуємо виконувати!
....
Продовжуємо виконувати!
Функція виконана!
У цьому полягає краса async await
, ваш код може продовжувати працювати, поки ви чекаєте чогось закінчити. Насправді у вас була б така функція, як функція виклику:
public async Task MyCallingFunction()
{
List<Task> myTasks = new List<Task>();
myTasks.Add(SaveRecords(GenerateNewRecords()));
myTasks.Add(SaveRecords2(GenerateNewRecords2()));
myTasks.Add(SaveRecords3(GenerateNewRecords3()));
myTasks.Add(SaveRecords4(GenerateNewRecords4()));
await Task.WhenAll(myTasks.ToArray());
}
Тут у вас є чотири різні функції збереження записів, що працюють одночасно . MyCallingFunction
завершиться набагато швидше, async await
ніж якщо б окремі SaveRecords
функції викликалися послідовно.
Єдине, чого я ще не торкався - це await
ключове слово. Це робить так, щоб зупинити виконання поточної функції, поки все, що Task
ви очікуєте, не завершиться. Отже, у випадку з оригіналом MyCallingFunction
, рядок Function Complete
не буде записаний на консоль, поки SaveRecords
функція не закінчиться.
Коротше кажучи, якщо у вас є можливість використовувати async await
, ви повинні, оскільки це значно підвищить ефективність вашої програми.