Як і коли використовувати "async" і "очікувати"


1065

З мого розуміння однієї з головних речей , які asyncіawait робить це , щоб зробити код легко писати і читати - але використовують їх рівний нерест фонових потоків для виконання довгою логіки тривалості?

Зараз я випробовую найосновніший приклад. Я додав кілька коментарів в рядку. Ви можете мені це уточнити?

// I don't understand why this method must be marked as `async`.
private async void button1_Click(object sender, EventArgs e)
{
    Task<int> access = DoSomethingAsync();
    // task independent stuff here

    // this line is reached after the 5 seconds sleep from 
    // DoSomethingAsync() method. Shouldn't it be reached immediately? 
    int a = 1; 

    // from my understanding the waiting should be done here.
    int x = await access; 
}

async Task<int> DoSomethingAsync()
{
    // is this executed on a background thread?
    System.Threading.Thread.Sleep(5000);
    return 1;
}

48
Також у своєму прикладі зауважте, що ви отримуєте попередження, коли компілюєте код вище. Зверніть увагу на попередження . Це говорить вам, що цей код не має сенсу.
Ерік Ліпперт

Відповіді:


759

При використанні asyncта awaitкомпілятор генерує кінцевий автомат в фоновому режимі.

Ось приклад, на якому я сподіваюся, що я можу пояснити деякі деталі на високому рівні, які тривають:

public async Task MyMethodAsync()
{
    Task<int> longRunningTask = LongRunningOperationAsync();
    // independent work which doesn't need the result of LongRunningOperationAsync can be done here

    //and now we call await on the task 
    int result = await longRunningTask;
    //use the result 
    Console.WriteLine(result);
}

public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation 
{
    await Task.Delay(1000); // 1 second delay
    return 1;
}

Що ж, що відбувається тут:

  1. Task<int> longRunningTask = LongRunningOperationAsync(); починає виконувати LongRunningOperation

  2. Самостійна робота робиться над припущенням, що головна нитка (ID теми = 1) await longRunningTaskбуде досягнута.

    Тепер, якщо завершення longRunningTaskне закінчилось і воно все ще працює, MyMethodAsync()повернеться до свого методу виклику, таким чином основний потік не блокується. Коли longRunningTaskце зроблено, то нитка з ThreadPool (може бути будь-якою ниткою) повернеться до MyMethodAsync()попереднього контексту і продовжить виконання (у цьому випадку друкуючи результат на консоль).

Другий випадок - це те, що longRunningTaskвже завершено його виконання, і результат доступний. При досягненні результату await longRunningTaskми вже маємо результат, тому код буде продовжувати виконуватись на тій самій нитці. (у цьому випадку результат друку на консоль). Звичайно, це не так у наведеному вище прикладі, коли вони Task.Delay(1000)задіяні.


65
Чому ми маємо "чекати" з "Завданням. Затримка (1000);" у методі асинхронізації LongRunningOperation?
Бенісон Сем

3
@codea У коментарях Еріка Ліпперта до статті він пов’язав вступну статтю з цією темою, де він спеціально порівнює стратегію DoEvents з async-wait
Каміло Мартінес

13
Тема @BenisonSam трохи стара, але у мене було те саме питання і я шукав відповідь. Причина "очікування" полягає в тому, що якщо ми опустимо "очікуємо", LongRunningOperationAsync () повернеться негайно. Насправді компілятор подасть попередження, якщо ми видалимо очікування. Повідомлення блогу Стівена Клірі blog.stephencleary.com/2011/09/… надає чудовий дизайн дискусій.
shelbypereira

70
Якщо кожен метод асинхронізації потребує очікування всередині нього, а очікування можна виконати лише на методах з асинхронізацією, коли він припиняється?
Бруно Сантос

108
Ця відповідь явно неправильна. Ці численні оновлення спричинить неправильне розуміння у багатьох користувачів. Документація MS чітко говорить про те, що жоден інший потік не використовується під час просто використання async, зачекайте. msdn.microsoft.com/en-us/library/mt674882.aspx Будь ласка, хтось виправить відповідь. Завдяки цьому я витрачав один цілий день.
Крішна Діпак

171

З мого розуміння, одна з головних речей, які асинхронізують і чекають, - це зробити код легким для запису та читання.

Вони мають зробити асинхронний код легким для запису та читання, так.

Це те саме, що нерестувати фонові нитки для виконання логіки тривалої тривалості?

Зовсім ні.

// Я не розумію, чому цей метод повинен бути позначений як "асинхронізація".

asyncКлючове слово дозволяє awaitключове слово. Тож будь-який метод, що використовує, awaitповинен бути позначений async.

// Цей рядок досягається після 5-секундного сну методом DoSomethingAsync (). Чи не слід було досягти негайно?

Ні, оскільки asyncметоди за замовчуванням не запускаються на іншому потоці.

// Чи виконується це на фоновій нитці?

Ні.


Можливо, вам буде корисно моє async/ awaitвступне . В офіційних документах MSDN також незвично добре (особливо TAP розділ), і asyncкоманда зробила відмінний FAQ .


6
Таким чином, він не працює на фоновому потоці, але також не блокується. Це можливо завдяки асинхронному API, який використовує зворотні дзвінки замість того, щоб перемикати з потоками. Ви ініціюєте операцію (I / O, socket, ..) і повертаєтесь до виконання своїх справ. Коли операція завершена, ОС буде викликати зворотний виклик. Це те, що робить Node.js або фреймворк Python Twisted, і вони також мають своє приємне пояснення.
Роман Плошил

3
"Ключове слово async дозволяє ключове слово очікування. Отже, будь-який метод, що використовує await, повинен бути позначений як асинхронізація.", - але чому? ця відповідь не допомагає зрозуміти, чому метод повинен бути позначений як асинхронний. Чи не може компілятор просто зробити висновок, що метод асинхронізується, заглянувши всередину на очікувані ключові слова?
Станіслав

9
@ Станіслав: У мене є запис у блозі, який стосується цього питання.
Стівен Клірі

3
Пропоноване уточнення: Ні, оскільки asyncметоди за замовчуванням не запускаються на іншому потоці. У вашому прикладі Sleep()виклик всередині DoSomethingAsync()блокує поточний потік, що запобігає продовженню виконання всередині, button1_Click()поки не DoSomethingAsync()завершиться. Зауважте, що поки Thread.Sleep()блокує виконуючу нитку,Task.Delay() does not.
DavidRR

166

Пояснення

Ось короткий приклад async/ awaitна високому рівні. Є набагато більше деталей, які слід врахувати поза цим.

Примітка: Task.Delay(1000) імітує виконання роботи протягом 1 секунди. Я думаю, що найкраще думати про це як очікування відповіді із зовнішнього ресурсу. Оскільки наш код чекає відповіді, система може відключити запущене завдання в бік і повернутися до нього, як тільки воно буде закінчено. Тим часом він може виконати якусь іншу роботу над цією ниткою.

У наведеному нижче прикладі перший блок робить саме це. Він починає всі завдання негайно ( Task.Delayрядки) і відкладає їх убік. Код буде робити паузу на await aрядку, поки не буде виконано затримку на 1 секунду до переходу до наступного рядка. Так як b, c, dі eвсе приступили до виконання майже в той же самий час , якa (з - за відсутність ОЧІКУВАННЯ), вони повинні закінчити приблизно в той же час в даному випадку.

У наведеному нижче прикладі другий блок починає завдання і чекає його закінчення (саме це і awaitробиться) перед початком наступних завдань. Кожна ітерація цього займає 1 секунду. TheawaitПід час паузи програми і чекати результату , перш ніж продовжити. Це основна відмінність першого та другого блоків.

Приклад

Console.WriteLine(DateTime.Now);

// This block takes 1 second to run because all
// 5 tasks are running simultaneously
{
    var a = Task.Delay(1000);
    var b = Task.Delay(1000);
    var c = Task.Delay(1000);
    var d = Task.Delay(1000);
    var e = Task.Delay(1000);

    await a;
    await b;
    await c;
    await d;
    await e;
}

Console.WriteLine(DateTime.Now);

// This block takes 5 seconds to run because each "await"
// pauses the code until the task finishes
{
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
}
Console.WriteLine(DateTime.Now);

ВИХІД:

5/24/2017 2:22:50 PM
5/24/2017 2:22:51 PM (First block took 1 second)
5/24/2017 2:22:56 PM (Second block took 5 seconds)

Додаткова інформація щодо SynchronizationContext

Примітка: саме тут для мене стає трохи туманним, тому, якщо я щось не можу, виправте мене, і я оновлю відповідь. Важливо мати базове розуміння того, як це працює, але ви можете отримати його, не будучи експертом, доки ви ніколи не використовуєтеConfigureAwait(false) , хоча, мабуть, ви втратите певну можливість для оптимізації.

Є один аспект цього, який робить async/ awaitконцепцію дещо складнішою для розуміння. Це той факт, що в цьому прикладі все це відбувається в одній і тій же нитці (або принаймні те, що, здається, є однаковою ниткою щодо її SynchronizationContext). За замовчуваннямawait відновить контекст синхронізації вихідного потоку, на якому він працював. Наприклад, у ASP.NET у вас HttpContextє прив'язка до потоку, коли надходить запит. Цей контекст містить речі, характерні для вихідного Http-запиту, наприклад, оригінальний об'єкт Request, який містить такі речі, як мова, IP-адреса, заголовки тощо Якщо ви переключите потоки на півдорозі, обробляючи щось, ви потенційно можете намагатися витягнути інформацію з цього об'єкта на іншийHttpContextщо може бути згубним. Якщо ви знаєте, що не будете використовувати контекст ні для чого, можете вибрати "не байдуже" про це. Це в основному дозволяє вашому коду працювати на окремому потоці, не зводячи до нього контекст.

Як ви цього досягаєте? За замовчуванням await a;код насправді робить припущення, що ви хочете захопити та відновити контекст:

await a; //Same as the line below
await a.ConfigureAwait(true);

Якщо ви хочете дозволити основний код продовжувати роботу в новій темі без початкового контексту, ви просто використовуєте false, а не true, щоб він знав, що не потрібно відновлювати контекст.

await a.ConfigureAwait(false);

Після того, як програма буде призупинена, вона потенційно продовжиться на зовсім іншій потоці з іншим контекстом. Ось звідки походить поліпшення продуктивності - воно може продовжуватися на будь-якій доступній нитці без відновлення початкового контексту, з якого він розпочався.

Це заплутано? В біса так! Ви можете це зрозуміти? Мабуть! Як тільки ви зрозумієте ці поняття, перейдіть до пояснень Стівена Клірі, які, як правило, більше спрямовані на когось із технічним розумінням async/ awaitвже.


Давайте скажемо, якщо всі ці завдання повертають Int і якщо я використовую результат першого завдання у другому завданні (або якийсь розрахунок), це було б неправильно?
veerendra gupta

3
@veerendragupta так. Ви б свідомо вирішили не запускати їх асинхронно в такому випадку (бо вони не асинхронні). Є ще декілька речей, які слід усвідомити щодо конфігураційного контексту, про який я тут не заходжу
Джо Філіпс,

Так await MethodCall()це абсолютна трата? Ви також можете скинути await/ async?
Вітані

2
@Jocie Не зовсім. Коли ви телефонуєте await, я думаю, що він випускає нитку назад до пулу, а не тримає її. Це робить його доступним для використання в іншому місці, очікуючи повернення Завдання
Джо Філіпс

2
@JoePhillips Я думаю, що те, що ви сказали, - це суть асинхронізації / очікування. Виклична нитка звільняється і може використовуватися іншими процесами на машині. Коли виклик очікування завершиться, новий потік використовується для відновлення того, що спочатку викликав абонент. Абонент ще чекає, але користь полягає в тому, що тим часом вивільняється нитка. В чому користь асинхронізації / очікування?
Боб Горн

147

Далі на інші відповіді погляньте на очікувати (C # Reference)

а конкретніше на прикладі, який включено, це трохи пояснює вашу ситуацію

Наведений нижче приклад Windows Forms ілюструє використання функції await в методі асинхронізації, WaitAsynchronmousAsync. Порівнюйте поведінку цього методу з поведінкою WaitSynchronous. Без оператора очікування, застосованого до завдання, WaitSynchronronic працює синхронно, незважаючи на використання модифікатора асинхроніки у своєму визначенні та виклику Thread.Sleep у своєму тілі.

private async void button1_Click(object sender, EventArgs e)
{
    // Call the method that runs asynchronously.
    string result = await WaitAsynchronouslyAsync();

    // Call the method that runs synchronously.
    //string result = await WaitSynchronously ();

    // Display the result.
    textBox1.Text += result;
}

// The following method runs asynchronously. The UI thread is not
// blocked during the delay. You can move or resize the Form1 window 
// while Task.Delay is running.
public async Task<string> WaitAsynchronouslyAsync()
{
    await Task.Delay(10000);
    return "Finished";
}

// The following method runs synchronously, despite the use of async.
// You cannot move or resize the Form1 window while Thread.Sleep
// is running because the UI thread is blocked.
public async Task<string> WaitSynchronously()
{
    // Add a using directive for System.Threading.
    Thread.Sleep(10000);
    return "Finished";
}

3
дякую за відповідь. Але чи WaitAsynchronmousAsync () виконується на окремому потоці?
Дан Діну

32
Я вірю так, що з розділу Очікування, що очікує, не блокує нитку, на якій він виконується. Натомість це змушує компілятор підписати решту методу асинхронізації як продовження очікуваного завдання. Потім управління повертається до абонента методу асинхронізації. Коли завдання завершується, він викликає його продовження, а виконання методу асинхронізації поновлюється там, де воно припинено.
Адріан Стандер

13
Згідно з цією статтею MSDN , "Ключові слова асинхронізації та очікування не викликають створення додаткових потоків .... метод асинхронізації не працює на власній темі". Я розумію, що у ключових ключових словах рамка пропускає вперед (назад до абонента), щоб дозволити запускати всі можливі незалежні коди, очікуючи закінчення тривалих операцій. Я думаю, що це означає, що після запуску всіх незалежних кодів, якщо довга операція не повернулася, вона заблокується. Я зараз це лише вчу.
Vimes

9
@astander Це неправильно. Він не виконується на іншій нитці. Це просто планує продовження (решта методу), яке потрібно викликати, коли таймер застосовується при Task.Delayпожежах.
MgSam

1
Ця відповідь неправильна через сон. Дивіться прийняту відповідь з очікуванням Завдання.Делай (1000); яка має правильну поведінку.
Jared Updike

62

Показуючи вищезазначені пояснення в дії в простій консольній програмі:

class Program
{
    static void Main(string[] args)
    {
        TestAsyncAwaitMethods();
        Console.WriteLine("Press any key to exit...");
        Console.ReadLine();
    }

    public async static void TestAsyncAwaitMethods()
    {
        await LongRunningMethod();
    }

    public static async Task<int> LongRunningMethod()
    {
        Console.WriteLine("Starting Long Running method...");
        await Task.Delay(5000);
        Console.WriteLine("End Long Running method...");
        return 1;
    }
}

А вихід:

Starting Long Running method...
Press any key to exit...
End Long Running method...

Таким чином,

  1. Головна запускає метод довгого запуску через TestAsyncAwaitMethods . Це негайно повертається, не зупиняючи поточну нитку, і ми відразу бачимо повідомлення "Натисніть будь-яку клавішу для виходу"
  2. Все це поки LongRunningMethodпрацює на задньому плані. Після її завершення інший потік з Threadpool підбирає цей контекст і відображає остаточне повідомлення

Таким чином, не блокується нитка.


"У якій частині виводу буде показано будь-яку клавішу для виходу ..."
StudioX

1
і яке використання (повернення 1)? це потрібно?
StudioX

1
@StudioX Я думаю, що у нього повинно бути ціле число повернення
Kuba Do

Я думаю, що return 1частина заслуговує додаткового пояснення: awaitключове слово дозволяє повернути базовий тип Task<T>безпосередньо, тим самим полегшуючи адаптувати свій вихідний код до світу очікування / асинхронізації . Але вам не потрібно повертати значення, оскільки це можливо повернути Taskбез вказівки типу, що повертається, що було б еквівалентом синхронного voidметоду. Зверніть увагу, що C # дозволяє async voidметоди, але вам слід уникати цього, якщо ви не вирішуєте обробників подій.
Крістіано Поцілунок

41

Я думаю, що ти вибрав поганий приклад System.Threading.Thread.Sleep

Точкою asyncзавдання є дозволити його виконувати у фоновому режимі, не блокуючи основну нитку, наприклад, виконуючи aDownloadFileAsync

System.Threading.Thread.Sleep це не те, що "робиться", воно просто спить, і тому наступний рядок досягається через 5 секунд ...

Читайте цю статтю, я вважаю, що це чудове пояснення asyncта awaitконцепція: http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx


3
Чому сон - це поганий приклад, але завантаження - хороший приклад. Це схоже на речі FooBar, коли я бачу Thread.Sleep Я розумію, що є якесь завдання, яке потребує часу. Я думаю, що його питання актуальне
Абдуррахім

1
@Abdurrahim Thread.Sleepблокує потік (потік не може робити нічого іншого, крім простою), але метод асинхронізації не робить. У випадку з DownloadFileAsyncпотоком можна перейти і зробити щось інше, поки відповідь не надійде з віддаленого сервера. Кращим заповнювачем для "якоїсь задачі, яка потребує часу" в методі асинхронізації Task.Delay, оскільки це насправді асинхронно.
Габріель

@GabrielLuci моє заперечення не стосується затримки проти сну; Ваша відповідь більше нагадує солом’яну відповідь; Якщо ви поставите це як коментар до питання, що я б нічого не заперечував, але як відповідь це більше нагадує солом'яну відповідь. Я думаю, що все-таки добре використовувати там асинхронізацію, навіть усі дзвінки, які він / вона повинен робити, будуть блокувати дзвінки; Це не призведе до недійсності всіх цілей ... Навіть усе, що залишилося, буде синтаксичним цукром, це вважається дійсним,
Абдуррахім,

1
Це не була моя відповідь. Але для вирішення вашого питання: це залежить від мети методу. Якщо він просто хотів викликати метод, він досяг успіху. Але в цьому випадку він намагався зробити метод, який працює асинхронно. Він зробив це, просто використовуючи asyncключове слово. Але його метод все ще працював синхронно, і ця відповідь прекрасно пояснила, чому: адже він насправді не запускав жодного асинхронного коду. Позначені способи asyncвсе ще працюють синхронно до тих пір, поки ви awaitне завершите Task. Якщо немає await, то метод працює синхронно, і компілятор попередить вас про це.
Габріель

23

Ось швидка консольна програма, щоб зрозуміти тим, хто слідує. TaskToDoМетод вашого довгий метод працює , що ви хочете зробити асінхра. Здійснення його запуску асинхронізацією виконується TestAsyncметодом. Метод тестових циклів просто виконує TaskToDoзавдання та виконує їх асинхронізацію. Ви можете бачити це в результатах, оскільки вони не завершуються в одному порядку від запуску до запуску - вони звітують до потоку інтерфейсу консолі, коли вони завершуються. Спрощений, але я думаю, що спрощені приклади виявляють серцевину шаблону краще, ніж більше причетних прикладів:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TestingAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            TestLoops();
            Console.Read();
        }

        private static async void TestLoops()
        {
            for (int i = 0; i < 100; i++)
            {
                await TestAsync(i);
            }
        }

        private static Task TestAsync(int i)
        {
            return Task.Run(() => TaskToDo(i));
        }

        private async static void TaskToDo(int i)
        {
            await Task.Delay(10);
            Console.WriteLine(i);
        }
    }
}

20

Для швидкого навчання ..

  • Зрозумійте потік виконання методу (з діаграмою): 3 хв

  • Самоаналіз питань (заради навчання): 1 хв

  • Швидко пройти через синтаксичний цукор: 5 хв

  • Поділіться плутаниною розробника: 5 хв

  • Проблема: Швидко змінити реалізацію звичайного коду в реальному світі на код Async: 2 хвилини

  • Куди далі?

Зрозумійте потік виконання методу (з діаграмою): 3 хв

У цьому зображенні просто зосередьтеся на №6 (нічого більше) введіть тут опис зображення

На кроці №6: Виконання тут зупинилося, оскільки закінчилося роботу. Щоб продовжити, йому потрібен результат від getStringTask (вид функції). Тому він використовує awaitоператор, щоб призупинити свій прогрес і повернути абоненту управління (вихід) (цього методу ми використовуємо). Фактичний виклик до getStringTask був здійснений раніше у # 2. У №2 було обіцяно повернути рядковий результат. Але коли це поверне результат? Чи повинні ми (# 1: AccessTheWebAsync) зробити другий дзвінок ще раз? Хто отримує результат, №2 (заява на виклик) або №6 (очікує заяву)

Зараз зовнішній абонент AccessTheWebAsync () також чекає. Тож абонент, який чекає AccessTheWebAsync, і AccessTheWebAsync на даний момент чекає GetStringAsync. Цікава річ, що AccessTheWebAsync зробив певну роботу перед очікуванням (# 4), можливо, щоб заощадити час від очікування. Така ж свобода багатозадачності також доступна для зовнішнього абонента (і всіх абонентів у ланцюжку), і це найбільший плюс цієї «асинхронної» речі! Ви відчуваєте, що це синхронно ... або нормально, але це не так.

Пам'ятайте, що метод уже повернуто (№2), він не може повернутися знову (не вдруге). Отже, як дізнається абонент? Це все про завдання! Завдання було прийнято. Завдання чекали (не метод, не значення). Значення буде встановлено у Завдання. Статус завдання буде встановлено для завершення. Абонент просто стежить за завданням (№6). Отже 6 # - це відповідь на те, де / хто отримує результат. Далі читайте для подальшого тут .

Самоаналіз питань заради навчання: 1 хв

Давайте трохи відрегулюємо питання:

Як і коли використовувати та ? asyncawait Tasks

Оскільки навчання Taskавтоматично охоплює інші два (і відповідає на ваше запитання)

Швидко пройти через синтаксичний цукор: 5 хв

  • До конверсії (оригінальний метод)

    internal static int Method(int arg0, int arg1) { int result = arg0 + arg1; IO(); // Do some long running IO. return result; }

  • метод, визначений завданням, для виклику вищевказаного методу

    internal static Task<int> MethodTask(int arg0, int arg1) { Task<int> task = new Task<int>(() => Method(arg0, arg1)); task.Start(); // Hot task (started task) should always be returned. return task; }

Ми згадали про очікування чи асинхронізацію? Ні. Зателефонуйте до описаного вище способу, і ви отримаєте завдання, яке ви зможете контролювати. Ви вже знаєте, що повертає завдання .. ціле число.

  • Виклик завдання - трохи складний, і саме тоді ключові слова починають з’являтися. Зателефонуємо MethodTask ()

    internal static async Task<int> MethodAsync(int arg0, int arg1) { int result = await HelperMethods.MethodTask(arg0, arg1); return result; }

Той самий код, що додається вище, як і зображення нижче: введіть тут опис зображення

  1. Ми чекаємо, що завдання буде завершено. Отжеawait
  2. Оскільки ми використовуємо функцію очікування, ми повинні використовувати async(обов'язковий синтаксис)
  3. MethodAsync з Asyncпрефіксом (стандарт кодування)

awaitлегко зрозуміти, але решта двох ( async, Async) може бути не :). Хоча, це має мати набагато більше сенсу для компілятора. Далі читайте тут для подальшого

Отже, є 2 частини.

  1. Створіть "Завдання"
  2. Створіть синтаксичний цукор для виклику завдання ( await+async)

Пам’ятайте, у нас був зовнішній абонент до AccessTheWebAsync (), і той абонент також не пощадив ... тобто він теж потрібен await+async. І ланцюг продовжується. Але завжди знайдеться Taskв одному кінці.

Все гаразд, але один розробник був здивований, побачивши, що №1 (Завдання) відсутній ...

Поділіться плутаниною розробника: 5 хв

Розробник помилився тим, що не реалізує, Taskале це все ще працює! Постарайтеся зрозуміти питання і просто прийняту відповідь, надану тут . Сподіваюся, ви прочитали і повністю зрозуміли. Підсумок полягає в тому, що ми можемо не бачити / реалізовувати "Завдання", але він реалізований десь у батьківському класі. Так само в нашому прикладі викликати вже побудований MethodAsync()спосіб набагато простіше, ніж реалізувати цей метод за допомогою Task( MethodTask()) себе. Більшість розробників важко обернутися Tasks, перетворюючи код на асинхронний.

Порада. Спробуйте знайти існуючу програму Async (як-от MethodAsyncабо ToListAsync), щоб передавати труднощі. Тому нам потрібно мати справу лише з Async і чекати (що легко і досить схоже на звичайний код)

Проблема: Швидко змінити реалізацію звичайного коду в режимі Async: 2 хвилини

Рядок коду, показаний нижче в шарі даних, почав розбиватися (багато місць). Оскільки ми оновили деякий наш код із .Net Framework 4.2. * До .Net core. Нам довелося виправити це за 1 годину впродовж усієї програми!

var myContract = query.Where(c => c.ContractID == _contractID).First();

Простенька!

  1. Ми встановили Nuget пакет EntityFramework, оскільки він має QueryableExtensions. Або іншими словами, це реалізація (завдання) Async, тому ми могли вижити просто Asyncі awaitв коді.
  2. простір імен = Microsoft.EntityFrameworkCore

Рядок коду виклику змінився так

var myContract = await query.Where(c => c.ContractID == _contractID).FirstAsync();
  1. Підпис методу змінено з

    Contract GetContract(int contractnumber)

    до

    async Task<Contract> GetContractAsync(int contractnumber)

  2. Метод виклику також постраждав: GetContractAsync(123456);був викликаний якGetContractAsync(123456).Result;

  3. Ми змінили його скрізь за 30 хвилин!

Але архітектор сказав нам не використовувати бібліотеку EntityFramework саме для цього! ой! драма! Тоді ми зробили власну реалізацію завдань (юк). Який ти вмієш. Ще легко! ..шти юк ..

Куди далі? Є чудове швидке відео, яке ми могли б подивитися про перетворення синхронізованих дзвінків в асинхронні в ядрі ASP.Net , можливо, це, мабуть, напрямок, який слід піти після прочитання цього.


фантастична відповідь! це мені допомогло тонну
cklimowski

1
Гарна відповідь. Ви можете просто виправити декілька дрібниць, таких як: (a) згадування про ".Net Framework 4.2" (не існує такої версії, яку я знаю) (b) обшивка в EntityFrameWork => EntityFramework
imitate

15

Усі відповіді тут використовують Task.Delay()або якусь іншу вбудовану asyncфункцію. Але ось мій приклад, який не використовує жодної з цих asyncфункцій:

// Starts counting to a large number and then immediately displays message "I'm counting...". 
// Then it waits for task to finish and displays "finished, press any key".
static void asyncTest ()
{
    Console.WriteLine("Started asyncTest()");
    Task<long> task = asyncTest_count();
    Console.WriteLine("Started counting, please wait...");
    task.Wait(); // if you comment this line you will see that message "Finished counting" will be displayed before we actually finished counting.
    //Console.WriteLine("Finished counting to " + task.Result.ToString()); // using task.Result seems to also call task.Wait().
    Console.WriteLine("Finished counting.");
    Console.WriteLine("Press any key to exit program.");
    Console.ReadLine();
}

static async Task<long> asyncTest_count()
{
    long k = 0;
    Console.WriteLine("Started asyncTest_count()");
    await Task.Run(() =>
    {
        long countTo = 100000000;
        int prevPercentDone = -1;
        for (long i = 0; i <= countTo; i++)
        {
            int percentDone = (int)(100 * (i / (double)countTo));
            if (percentDone != prevPercentDone)
            {
                prevPercentDone = percentDone;
                Console.Write(percentDone.ToString() + "% ");
            }

            k = i;
        }
    });
    Console.WriteLine("");
    Console.WriteLine("Finished asyncTest_count()");
    return k;
}

2
Дякую! перша відповідь, яка насправді виконує якусь роботу, а не чекати.
Jeffnl

дякую, що показали task.Wait();і як це можна використовувати, щоб уникнути асинхронізації / очікування пекла: P
кодер

12

Ця відповідь спрямована на надання деякої інформації, характерної для ASP.NET.

Використовуючи асинхронізування / очікування в контролері MVC, можна збільшити використання пулу потоків і досягти набагато кращої пропускної здатності, як пояснено у статті нижче,

http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4

У веб-додатках, які бачать велику кількість одночасних запитів під час запуску або мають бурхливе навантаження (де паралельність зростає раптово), здійснення цих викликів веб-служб асинхронними підвищить чутливість вашої програми. Асинхронний запит обробляє стільки ж часу, скільки і синхронний запит. Наприклад, якщо запит здійснює дзвінок веб-служби, для виконання якого потрібні дві секунди, запит займає дві секунди, виконується він синхронно або асинхронно. Однак під час асинхронного дзвінка нитка не блокується відповідати на інші запити, поки вона чекає завершення першого запиту. Тому асинхронні запити запобігають черзі запитів та росту пулу потоків, коли існує багато паралельних запитів, які викликають тривалі операції.


12

Асинхронізація та очікування простого пояснення

Проста аналогія

Людина може чекати свого ранкового поїзда. Це все, що вони роблять, оскільки це їх основне завдання, яке вони виконують в даний час. (синхронне програмування (що ти зазвичай робиш!))

Інша людина може чекати свого ранкового поїзда, поки вони викурять сигарету, а потім п’ють каву. (Асинхронне програмування)

Що таке асинхронне програмування?

Асинхронне програмування - це те, коли програміст вирішить запустити частину свого коду окремим потоком від основного потоку виконання, а потім сповістить основний потік про його завершення.

Що насправді робить ключове слово async?

Префіксація ключового слова async до назви методу типу

async void DoSomething(){ . . .

дозволяє програмісту використовувати ключове слово очікування під час виклику асинхронних завдань. Це все, що робить.

Чому це важливо?

У багатьох програмних системах основний потік зарезервований для операцій, що стосуються спеціально інтерфейсу користувача. Якщо у мене запущений дуже складний рекурсивний алгоритм, який займає 5 секунд для завершення роботи на моєму комп’ютері, але я запускаю це на головній нитці (потік інтерфейсу користувача) Коли користувач намагається клацнути що-небудь на моєму додатку, воно виявиться замороженим оскільки моя основна нитка вийшла в чергу і наразі обробляє занадто багато операцій. В результаті головний потік не може обробити клацання миші, щоб запустити метод з натискання кнопки.

Коли ви використовуєте Async та Await?

Використовуйте асинхронні ключові слова в ідеалі, коли ви робите все, що не включає інтерфейс користувача.

Тож скажемо, що ви пишете програму, яка дозволяє користувачеві робити ескізи на своєму мобільному телефоні, але кожні 5 секунд він перевірятиме погоду в Інтернеті.

Нам слід чекати дзвінка на виклики опитувальників кожні 5 секунд, щоб отримати погоду, оскільки користувачеві програми потрібно постійно взаємодіяти з мобільним сенсорним екраном, щоб зробити гарні знімки.

Як ви використовуєте Async та Await

Виходячи з наведеного вище прикладу, ось якийсь псевдокод, як його написати:

    //ASYNCHRONOUS
    //this is called using the await keyword every 5 seconds from a polling timer or something.

    async Task CheckWeather()
    {
        var weather = await GetWeather();
        //do something with the weather now you have it
    }

    async Task<WeatherResult> GetWeather()
    {

        var weatherJson = await CallToNetworkAddressToGetWeather();
        return deserializeJson<weatherJson>(weatherJson);
    }

    //SYNCHRONOUS
    //This method is called whenever the screen is pressed
    void ScreenPressed()
    {
        DrawSketchOnScreen();
    }

Додаткові примітки - оновлення

Я забув згадати в своїх оригінальних записках, що в C # можна очікувати лише методів, загорнутих у Завдання. наприклад, ви можете чекати цього методу:

// awaiting this will return a string.
// calling this without await (synchronously) will result in a Task<string> object.
async Task<string> FetchHelloWorld() {..

Ви не можете чекати методів, які не є такими завданнями:

async string FetchHelloWorld() {..

Сміливо перегляньте вихідний код класу Завдання тут .


4
Дякуємо, що знайшли час для написання цього.
Прашант

10

Асинхрон / Чекай

Насправді Async / Await - це пара ключових слів, які є лише синтаксичним цукром для створення зворотного виклику асинхронного завдання.

Візьмемо для прикладу цю операцію:

    public static void DoSomeWork()
    {
        var task = Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS NOT bubbling up due to the different threads
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // This is the callback
        task.ContinueWith((t) => {
            // -> Exception is swallowed silently
            Console.WriteLine("Completed");

            // [RUNS ON WORKER THREAD]
        });
    }

У наведеному вище коді є ряд недоліків. Помилки не передаються, і їх важко читати. Але Асинх і Очікуйте, щоб допомогти нам:

    public async static void DoSomeWork()
    {
        var result = await Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS bubbling up
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // every thing below is a callback 
        // (including the calling methods)

        Console.WriteLine("Completed");

    }

Очікування дзвінків повинно бути в методах Async. Це має деякі переваги:

  • Повертає результат Завдання
  • автоматично створює зворотний дзвінок
  • перевіряє помилки та дає їм змогу перетворюватися на виклик (лише до дзвінків, які не чекають у стовпі викликів)
  • чекає результату
  • звільняє основну нитку
  • виконує зворотний виклик на головному потоці
  • для виконання завдання використовує робочу нитку з нитки
  • робить код легким для читання
  • і багато іншого

ПРИМІТКА : Async і Await використовуються для асинхронних викликів, щоб не робити цих. Для цього ви повинні використовувати " Бібліотеку завдань" , як і Task.Run ().

Ось порівняння між очікуваними та не очікуваними рішеннями

Це рішення без асинхронізації:

    public static long DoTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]
        var task = Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        // goes directly further
        // WITHOUT waiting until the task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 50 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

Це метод асинхронізації:

    public async static Task<long> DoAwaitTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]

        await Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        // Waits until task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 2050 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

Ви можете фактично викликати метод асинхронізації без ключового слова, що очікує, але це означає, що будь-яке Виняток тут проковтується в режимі випуску:

    public static Stopwatch stopWatch { get; } = new Stopwatch();

    static void Main(string[] args)
    {
        Console.WriteLine("DoAwaitTask: " + DoAwaitTask().Result + " ms");
        // 2050 (2000 more because of the await)
        Console.WriteLine("DoTask: " + DoTask() + " ms");
        // 50
        Console.ReadKey();
    }

Async і Await не призначені для паралельних обчислень. Вони використовуються, щоб не блокувати вашу основну нитку. Якщо мова йде про програми asp.net або Windows, блокування вашої основної теми через мережевий дзвінок - це погано. Якщо ви це зробите, ваш додаток не відповість або навіть вийде з ладу.

Перегляньте документи MS для отримання додаткових прикладів.


9

Якщо чесно, я все-таки вважаю, що найкраще пояснення - це майбутнє та обіцянки у Вікіпедії: http://en.wikipedia.org/wiki/Futures_and_promises

Основна ідея полягає в тому, що у вас є окремий пул потоків, який виконує завдання асинхронно. При його використанні. Однак об'єкт обіцяє, що він виконає операцію в якийсь час і дасть результат, коли ви її запитаєте. Це означає, що він буде заблокований, коли ви запитаєте результат і ще не завершився, але виконати в пулі потоків інакше.

Звідти ви можете оптимізувати речі: деякі операції можна реалізувати асинхронізувати, а ви можете оптимізувати такі речі, як файли вводу-виводу та мережевий зв’язок, об'єднавши наступні запити та / або упорядкувавши їх. Я не впевнений, чи це вже є в рамках завдань Microsoft, але якщо це не так, це було б одним із перших речей, які я додам.

Ви можете реально реалізувати майбутні сортування шаблонів із врожайністю в C # 4.0. Якщо ви хочете знати, як воно працює, я можу порекомендувати це посилання, яке працює гідно: http://code.google.com/p/fracture/source/browse/trunk/Squared/TaskLib/ . Однак, якщо ви почнете грати з ним самостійно, ви помітите, що вам дійсно потрібна підтримка мови, якщо ви хочете робити всі класні речі - саме це і зробила Microsoft.


8

Дивіться цю скрипку https://dotnetfiddle.net/VhZdLU (та покращуйте її, якщо можливо) для запуску простого консольного додатка, який показує використання Task, Task.WaitAll (), асинхронізації та очікування операторів в одній програмі.

Ця скрипка повинна очистити вашу концепцію циклу виконання.

Ось зразок коду

using System;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {               
        var a = MyMethodAsync(); //Task started for Execution and immediately goes to Line 19 of the code. Cursor will come back as soon as await operator is met       
        Console.WriteLine("Cursor Moved to Next Line Without Waiting for MyMethodAsync() completion");
        Console.WriteLine("Now Waiting for Task to be Finished");       
        Task.WaitAll(a); //Now Waiting      
        Console.WriteLine("Exiting CommandLine");       
    }

    public static async Task MyMethodAsync()
    {
        Task<int> longRunningTask = LongRunningOperation();
        // independent work which doesn't need the result of LongRunningOperationAsync can be done here
        Console.WriteLine("Independent Works of now executes in MyMethodAsync()");
        //and now we call await on the task 
        int result = await longRunningTask;
        //use the result 
        Console.WriteLine("Result of LongRunningOperation() is " + result);
    }

    public static async Task<int> LongRunningOperation() // assume we return an int from this long running operation 
    {
        Console.WriteLine("LongRunningOperation() Started");
        await Task.Delay(2000); // 2 second delay
        Console.WriteLine("LongRunningOperation() Finished after 2 Seconds");
        return 1;
    }   

}

Сліди, що надходять із вихідного вікна: введіть тут опис зображення


3
public static void Main(string[] args)
{
    string result = DownloadContentAsync().Result;
    Console.ReadKey();
}

// You use the async keyword to mark a method for asynchronous operations.
// The "async" modifier simply starts synchronously the current thread. 
// What it does is enable the method to be split into multiple pieces.
// The boundaries of these pieces are marked with the await keyword.
public static async Task<string> DownloadContentAsync()// By convention, the method name ends with "Async
{
    using (HttpClient client = new HttpClient())
    {
        // When you use the await keyword, the compiler generates the code that checks if the asynchronous operation is finished.
        // If it is already finished, the method continues to run synchronously.
        // If not completed, the state machine will connect a continuation method that must be executed WHEN the Task is completed.


        // Http request example. 
        // (In this example I can set the milliseconds after "sleep=")
        String result = await client.GetStringAsync("http://httpstat.us/200?sleep=1000");

        Console.WriteLine(result);

        // After completing the result response, the state machine will continue to synchronously execute the other processes.


        return result;
    }
}

3

На більш високому рівні:

1) Ключове слово Async дозволяє очікувати, і це все, що він робить. Ключове слово Async не запускає метод в окремий потік. Метод початку f async працює синхронно, доки він не чекає на виконання трудомісткого завдання.

2) Ви можете чекати на метод, який повертає Завдання або Завдання типу T. Ви не можете чекати методу асинхронізації void.

3) Моменти, що зустрічаються з основною ниткою, очікують на трудомістке завдання або при запуску фактичної роботи головний потік повертається до абонента поточного методу.

4) Якщо головний потік бачить очікування на завдання, яке ще виконується, він не чекає його і повертається до абонента поточного методу. Таким чином додаток залишається чуйним.

5) Чекаємо на завдання з обробки, тепер буде виконано на окремому потоці з пулу потоків.

6) Коли ця задача очікування буде виконана, весь код під нею буде виконаний окремим потоком

Нижче наведено зразок коду. Виконайте його і перевірте ідентифікатор потоку

using System;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncAwaitDemo
{
    class Program
    {
        public static async void AsynchronousOperation()
        {
            Console.WriteLine("Inside AsynchronousOperation Before AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            //Task<int> _task = AsyncMethod();
            int count = await AsyncMethod();

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod Before Await, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            //int count = await _task;

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await Before DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            DependentMethod(count);

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await After DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
        }

        public static async Task<int> AsyncMethod()
        {
            Console.WriteLine("Inside AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            int count = 0;

            await Task.Run(() =>
            {
                Console.WriteLine("Executing a long running task which takes 10 seconds to complete, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(20000);
                count = 10;
            });

            Console.WriteLine("Completed AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            return count;
        }       

        public static void DependentMethod(int count)
        {
            Console.WriteLine("Inside DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId + ". Total count is " + count);
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Started Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            AsynchronousOperation();

            Console.WriteLine("Completed Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            Console.ReadKey();
        }

    }
}

2

Наскільки я це розумію, у суміш має бути доданий третій термін: Task .

Async це лише класифікатор, який ви ставите на свій метод, щоб сказати, що це асинхронний метод.

Task це повернення async функції. Він виконується асинхронно.

ви await завдання. Коли виконання коду досягне цього рядка, керування відскакує назад на абонент навколишньої початкової функції.

Якщо замість цього, ви призначаєте повернення asyncфункції (тобто Task) змінної, коли виконання коду досягає цього рядка, воно просто продовжується повз цей рядок у навколишній функції, тоді як виконання Taskвиконується асинхронно.


1

використовує їх рівне нерестовому фоновим потокам для виконання логіки тривалості?

Ця стаття MDSN: Асинхронне програмування за допомогою асинхронізації та очікування (C #) пояснює це прямо:

Ключові слова асинхронізації та очікування не викликають створення додаткових потоків. Методи асинхронізації не вимагають багатопотокових записів, оскільки метод асинхронізації не працює на власній потоці. Метод працює у поточному контексті синхронізації та використовує час у потоці лише тоді, коли метод активний.


1

У наступному коді метод HttpClient GetByteArrayAsync повертає завдання, getContentsTask. Завдання - це обіцянка створити фактичний масив байтів, коли завдання буде виконано. Оператор очікування застосовується до getContentsTask, щоб призупинити виконання в SumPageSizesAsync до завершення getContentsTask. Тим часом контроль повертається абонентові SumPageSizesAsync. Після завершення getContentsTask вираз очікування оцінюється до масиву байтів.

private async Task SumPageSizesAsync()
{
    // To use the HttpClient type in desktop apps, you must include a using directive and add a 
    // reference for the System.Net.Http namespace.
    HttpClient client = new HttpClient();
    // . . .
    Task<byte[]> getContentsTask = client.GetByteArrayAsync(url);
    byte[] urlContents = await getContentsTask;

    // Equivalently, now that you see how it works, you can write the same thing in a single line.
    //byte[] urlContents = await client.GetByteArrayAsync(url);
    // . . .
}

1

Нижче наведено код, який читає файл excel, відкриваючи діалогове вікно, а потім використовує асинхронізацію та чекає запуску асинхронного коду, який читає по одному рядку від excel та пов'язує з сіткою

namespace EmailBillingRates
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            lblProcessing.Text = "";
        }

        private async void btnReadExcel_Click(object sender, EventArgs e)
        {
            string filename = OpenFileDialog();

            Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
            Microsoft.Office.Interop.Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(filename);
            Microsoft.Office.Interop.Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1];
            Microsoft.Office.Interop.Excel.Range xlRange = xlWorksheet.UsedRange;
            try
            {
                Task<int> longRunningTask = BindGrid(xlRange);
                int result = await longRunningTask;

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message.ToString());
            }
            finally
            {
                //cleanup  
               // GC.Collect();
                //GC.WaitForPendingFinalizers();

                //rule of thumb for releasing com objects:  
                //  never use two dots, all COM objects must be referenced and released individually  
                //  ex: [somthing].[something].[something] is bad  

                //release com objects to fully kill excel process from running in the background  
                Marshal.ReleaseComObject(xlRange);
                Marshal.ReleaseComObject(xlWorksheet);

                //close and release  
                xlWorkbook.Close();
                Marshal.ReleaseComObject(xlWorkbook);

                //quit and release  
                xlApp.Quit();
                Marshal.ReleaseComObject(xlApp);
            }

        }

        private void btnSendEmail_Click(object sender, EventArgs e)
        {

        }

        private string OpenFileDialog()
        {
            string filename = "";
            OpenFileDialog fdlg = new OpenFileDialog();
            fdlg.Title = "Excel File Dialog";
            fdlg.InitialDirectory = @"c:\";
            fdlg.Filter = "All files (*.*)|*.*|All files (*.*)|*.*";
            fdlg.FilterIndex = 2;
            fdlg.RestoreDirectory = true;
            if (fdlg.ShowDialog() == DialogResult.OK)
            {
                filename = fdlg.FileName;
            }
            return filename;
        }

        private async Task<int> BindGrid(Microsoft.Office.Interop.Excel.Range xlRange)
        {
            lblProcessing.Text = "Processing File.. Please wait";
            int rowCount = xlRange.Rows.Count;
            int colCount = xlRange.Columns.Count;

            // dt.Column = colCount;  
            dataGridView1.ColumnCount = colCount;
            dataGridView1.RowCount = rowCount;

            for (int i = 1; i <= rowCount; i++)
            {
                for (int j = 1; j <= colCount; j++)
                {
                    //write the value to the Grid  
                    if (xlRange.Cells[i, j] != null && xlRange.Cells[i, j].Value2 != null)
                    {
                         await Task.Delay(1);
                         dataGridView1.Rows[i - 1].Cells[j - 1].Value =  xlRange.Cells[i, j].Value2.ToString();
                    }

                }
            }
            lblProcessing.Text = "";
            return 0;
        }
    }

    internal class async
    {
    }
}

0

Відповіді тут корисні як загальні вказівки щодо очікування / асинхронізації. Вони також містять детальну інформацію про те, як очікувати / асинхронізуватись. Я хотів би поділитися з вами деяким практичним досвідом, який ви повинні знати, перш ніж використовувати цю модель дизайну.

Термін "очікувати" є буквальним, тому будь-яка нитка, на яку ви його закликаєте, дочекається результату методу, перш ніж продовжувати. На передньому плані це катастрофа . Нитка переднього плану несе тягар створення вашої програми, включаючи перегляди, перегляд моделей, початкову анімацію та все інше, що ви завантажили з цими елементами. Тож, коли ви чекаєте нитки переднього плану, ви зупиняєте додаток. Користувач чекає і чекає, коли нічого, здається, не відбудеться. Це забезпечує негативну роботу користувачів.

Ви, звичайно, можете чекати фонову нитку, використовуючи різні засоби:

Device.BeginInvokeOnMainThread(async () => { await AnyAwaitableMethod(); });

// Notice that we do not await the following call, 
// as that would tie it to the foreground thread.
try
{
Task.Run(async () => { await AnyAwaitableMethod(); });
}
catch
{}

Повний код цих зауважень знаходиться на веб-сторінці https://github.com/marcusts/xamarin-forms-annoyances . Дивіться рішення під назвою AwaitAsyncAntipattern.sln.

Сайт GitHub також надає посилання на більш детальну дискусію з цієї теми.


1
Як я розумію, async / awaitце синтаксичний цукор для зворотних викликів, він не має нічого спільного з нанизуванням. msdn.microsoft.com/en-us/magazine/hh456401.aspx Це для коду, не пов'язаного з процесором, наприклад, очікування введення чи затримки. Task.Runслід використовувати лише для пов'язаного з процесором коду blog.stephencleary.com/2013/10/…
geometrikal

The term "await" is literal, so whatever thread you call it on will wait for the result of the method before continuing.Це неправда - можливо, ви мали на увазі Task.Wait ()? Коли ви використовуєте await, він встановлює решту методу як продовження, яке слід виконувати, коли все, що ви очікуєте, завершено. Він закриває метод, в якому ви його використовували, тому абонент може продовжувати. Тоді, коли рядок очікування-редакції фактично закінчений, він закінчує залишок цього методу на деякій нитці (зазвичай це робоча нитка).
Don Cheadle

@geometrikal, по суті, async/awaitполягає у звільненні .NET Threads. Коли ви awaitвиконуєте дійсно-асинхронну операцію (наприклад, .NET's File.WriteAsync), вона призупиняє залишок методу, який ви використовували await, тому абонент може продовжувати та потенційно закінчувати свою мету. Немає блокування потоку або очікування awaitоперації -ed. Коли операція, яку ви awaitредагували, завершена, решта async/awaitметоду розміщується на потоці та виконується (подібно до ідеї зворотного виклику).
Don Cheadle
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.