Как и когда использовать «async» и «await»


1067

Из моего понимания одной из главных вещей , которые 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. Независимая работа сделана, давайте предположим, что основной поток (Thread ID = 1) тогда await longRunningTaskдостигнут.

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

Вторым случаем может быть то, что longRunningTaskон уже завершил свое выполнение, и результат доступен. При достижении await longRunningTaskмы уже имеем результат, поэтому код будет продолжать выполняться в том же потоке. (в этом случае результат печати выводится на консоль). Конечно, это не относится к приведенному выше примеру, где есть Task.Delay(1000)участие.


65
Почему мы «ждем» с «Task.Delay (1000);» в LongRunningOperation асинхронный метод?
Benison Sam

3
@codea В комментариях Эрика Липперта к статье он связал вступительную статью с этой темой, где он специально сравнивает стратегию DoEvents с async-await
Камило Мартинес,

13
Тема @BenisonSam немного устарела, но у меня был тот же вопрос, и я искал ответ. Причина для «ожидания» заключается в том, что если мы опускаем «ожидание», LongRunningOperationAsync () немедленно вернется. Фактически, компилятор выдаст предупреждение, если мы удалим ожидание. Сообщение Стивена Клири в блоге blog.stephencleary.com/2011/09/… дает отличные обсуждения дизайна.
Шелбиперейра

70
Если каждый асинхронный метод должен иметь внутри него ожидание, и ожидание может быть сделано только для методов с асинхронностью, когда он остановится?
Бруно Сантос

108
Этот ответ явно неверен. Эти многочисленные отзывы вызовут неправильное понимание у многих пользователей. Документация MS ясно говорит, что никакой другой поток не используется при использовании только async, подождите. msdn.microsoft.com/en-us/library/mt674882.aspx Пожалуйста, кто-нибудь исправит ответ. Из-за этого я потратил впустую целый день.
Кришна Дипак

171

Насколько я понимаю, одна из главных вещей, которую делают async и await, - это сделать код легким для написания и чтения.

Они должны сделать асинхронный код простым для написания и чтения, да.

Это то же самое, что порождение фоновых потоков для выполнения длительной логики?

Не за что.

// Я не понимаю, почему этот метод должен быть помечен как «асинхронный».

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. Поэтому любой метод, использующий await, должен быть помечен как async.», - но почему? Этот ответ не помогает понять, почему метод должен быть помечен как асинхронный. Разве компилятор не может просто определить, что метод асинхронный, заглянув внутрь на ключевые слова await?
Станислав

9
@ Станислав: У меня есть запись в блоге , посвященная этому вопросу.
Стивен Клири

3
Предлагаемое пояснение: Нет, потому что asyncметоды не запускаются в другом потоке по умолчанию. В вашем примере, Sleep()вызов внутри DoSomethingAsync()блокирует текущий поток, который препятствует продолжению выполнения button1_Click()до DoSomethingAsync()завершения. Обратите внимание, что while 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 секунду. Программа awaitприостанавливает работу программы и ждет результата, прежде чем продолжить. В этом главное отличие первого и второго блоков.

пример

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, такие как исходный объект запроса, который имеет такие вещи, как язык, IP-адрес, заголовки и т. Д. Если вы переключаете потоки на полпути при обработке чего-либо, вы можете в конечном итоге попытаться извлечь информацию из этого объекта на другомHttpContextчто может быть катастрофическим. Если вы знаете, что ни для чего не будете использовать контекст, вы можете выбрать «не заботиться» об этом. Это в основном позволяет вашему коду запускаться в отдельном потоке, не привнося в него контекст.

Как вы этого добиваетесь? По умолчанию await a;код фактически предполагает, что вы действительно хотите захватить и восстановить контекст:

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

Если вы хотите, чтобы основной код продолжал работу в новом потоке без исходного контекста, вы просто используете false вместо true, чтобы он знал, что не нужно восстанавливать контекст.

await a.ConfigureAwait(false);

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

Это что-то смущает? Да, черт возьми! Вы можете понять это? Вероятно! Как только вы овладеете концепциями, перейдите к объяснениям Стивена Клири, которые, как правило, больше ориентированы на кого-то, у кого уже есть техническое понимание async/ awaitуже.


Скажем, если все эти задачи возвращают int, и если я использую результат первой задачи во второй задаче (или какой-то расчет), это было бы неправильно?
Веерендра Гупта

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

Так await MethodCall()это абсолютная трата? Вы могли бы также бросить await/ async?
Витани

2
@ Джоси Не совсем. Когда вы звоните await, я думаю, что он освобождает поток обратно в пул, а не удерживает его. Это делает его доступным для использования в другом месте, ожидая возвращения задания
Джо Филлипс

2
@JoePhillips Я думаю, что вы только что сказали, это суть async / await. Вызывающий поток освобождается и может использоваться другими процессами на машине. Когда вызов await завершен, используется новый поток, чтобы возобновить то, что изначально начал вызывающий. Вызывающая сторона все еще ждет, но преимущество заключается в том, что поток тем временем освобождается. В этом преимущество асинхронности / ожидания?
Боб Хорн

147

В дополнение к другим ответам взгляните на await (C # Reference)

и более конкретно в приведенном примере, это немного объясняет вашу ситуацию

В следующем примере Windows Forms показано использование await в асинхронном методе WaitAsynchronouslyAsync. Сравните поведение этого метода с поведением WaitSynchronously. Без оператора await, применяемого к задаче, WaitSynchronously выполняется синхронно, несмотря на использование модификатора async в его определении и вызова 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
Спасибо за ответ. Но WaitAsynchronouslyAsync () выполняется в отдельном потоке?
Дан Дину

32
Я верю в это из раздела «Ожидание» не блокирует поток, в котором оно выполняется. Вместо этого он заставляет компилятор зарегистрировать оставшуюся часть асинхронного метода как продолжение ожидаемой задачи. Затем управление возвращается к вызывающей стороне асинхронного метода. Когда задача завершается, она вызывает ее продолжение, и выполнение асинхронного метода возобновляется с того места, где оно было остановлено.
Адриан Стандер

13
Согласно этой статье MSDN , «ключевые слова async и await не приводят к созданию дополнительных потоков .... асинхронный метод не выполняется в своем собственном потоке». Насколько я понимаю, при ожидании ключевых слов платформа пропускает вперед (обратно к вызывающей стороне), чтобы позволить всему возможному независимому коду выполняться в ожидании завершения длинных операций. Я думаю, это означает, что после запуска всего независимого кода, если длинная операция не вернулась, он заблокируется. Я сейчас только учусь этому.
Vimes

9
@astander Это неправильно. Он не выполняется в другом потоке. Он просто планирует продолжение (оставшуюся часть метода) для вызова при Task.Delayсрабатывании таймера .
MgSam

1
Этот ответ неверен из-за сна. Смотрите принятый ответ с await Task.Delay (1000); который имеет правильное поведение.
Джаред Апдайк

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. Main запускает длительный метод через TestAsyncAwaitMethods. Это немедленно возвращается без остановки текущего потока, и мы сразу же видим сообщение «Нажмите любую клавишу для выхода»
  2. Все это время LongRunningMethodработает в фоновом режиме. После его завершения другой поток из Threadpool выбирает этот контекст и отображает последнее сообщение.

Таким образом, не поток заблокирован.


«Нажмите любую клавишу для выхода ...» будет показано, в какой части вывода?
StudioX

1
и что за использование (возврат 1)? это необходимо?
StudioX

1
@StudioX я думаю, что он должен иметь целое число возвращаемого типа
Куба До

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

41

Я думаю, что вы выбрали плохой пример с System.Threading.Thread.Sleep

Суть asyncзадачи состоит в том, чтобы он выполнялся в фоновом режиме, не блокируя основной поток, например, делаяDownloadFileAsync

System.Threading.Thread.Sleep это не то, что «делается», это просто спит, и поэтому ваша следующая строка достигается через 5 секунд ...

Прочитайте эту статью, я думаю, что это отличное объяснение asyncи awaitконцепция: http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx


3
Почему Sleep - плохой пример, а Download - хороший пример. Это похоже на FooBar, когда я вижу Thread.Sleep, я понимаю, что есть какая-то задача, которая требует времени. Я думаю, что его вопрос актуален
Абдуррахим

1
@Abdurrahim Thread.Sleepблокирует поток (поток не может делать ничего другого, кроме простоя), но асинхронный метод этого не делает. В случае DownloadFileAsync, поток может пойти и сделать что-то еще, пока не придет ответ от удаленного сервера. Лучшим заполнителем для «некоторой задачи, которая требует времени» в асинхронном методе, является то Task.Delay, что это на самом деле асинхронно.
Габриэль Люси

@GabrielLuci мое возражение не касается задержки против сна; Ваш ответ больше похож на ответ соломенного чучела; Если вы добавите это в качестве комментария к вопросу, на который я ничего не смогу возразить, но как ответ, он будет больше похож на ответ соломенного чучела. Я думаю, что все еще нормально использовать асинхронный вызов, даже если все вызовы, которые он / она должен сделать, будут блокировать вызовы; Это не лишит законной силы всю цель ... Даже все, что осталось, будет синтаксическим сахаром, который он считает действительным,
Абдуррахим

1
Это был не мой ответ. Но для решения вашей точки зрения: это зависит от цели метода. Если он просто хотел вызвать метод, он преуспел. Но в этом случае он пытался создать метод, который работает асинхронно. Он сделал это, просто используя asyncключевое слово. Но его метод все еще работал синхронно, и этот ответ прекрасно объяснил почему: потому что он фактически не выполнял асинхронный код. Отмеченные методы по- asyncпрежнему работают синхронно до тех пор, пока вы awaitне завершите Task. Если нет await, метод работает синхронно, и компилятор предупредит вас об этом.
Габриэль Люси

23

Вот быстрая консольная программа, чтобы она была понятна тем, кто следит за ней. TaskToDoМетод вашего длинный метод работает , что вы хотите сделать асинхра. Выполнение асинхронного запуска выполняется TestAsyncметодом. Метод test loop просто выполняет 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);
        }
    }
}

22

Для быстрого обучения ..

  • Разобраться в потоке выполнения метода (с диаграммой): 3 минуты

  • Вопрос самоанализ (ради обучения): 1 мин

  • Быстро пройти через синтаксис сахара: 5 минут

  • Поделитесь смущением разработчика: 5 минут

  • Проблема: Быстрое изменение реальной реализации нормального кода на асинхронный код: 2 минуты

  • Куда дальше?

Разобраться в потоке выполнения метода (с диаграммой): 3 минуты

На этом изображении, просто сфокусируйтесь на # 6 (больше ничего) введите описание изображения здесь

На шаге № 6: выполнение здесь остановлено, так как оно закончилось без работы. Для продолжения нужен результат из getStringTask (вид функции). Следовательно, он использует awaitоператор, чтобы приостановить свой прогресс и вернуть управление (yield) вызывающей стороне (в этом методе мы находимся). Фактический вызов 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; }

  • Task-ified метод для вызова вышеуказанного метода

    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; }

Мы упомянули await или async? Нет. Вызовите вышеуказанный метод, и вы получите задачу, которую можете отслеживать. Вы уже знаете, что возвращает задание .. целое число.

  • Вызов задачи немного сложен, и именно тогда ключевые слова начинают появляться. Давайте назовем MethodTask ()

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

Тот же код выше добавлен как изображение ниже: введите описание изображения здесь

  1. Мы «ожидаем» задачи, которая будет завершена. Следовательноawait
  2. Так как мы используем await, мы должны использовать async (обязательный синтаксис)
  3. MethodAsync с Asyncпрефиксом (стандарт кодирования)

awaitэто легко понять, но остальные два ( async, Async) могут не быть :). Ну, это должно сделать намного больше смысла компилятор though.Further читает для последующего использования здесь

Итак, есть 2 части.

  1. Создать «Задача»
  2. Создать синтаксический сахар для вызова задачи ( await+async)

Помните, у нас был внешний вызывающий объект для AccessTheWebAsync (), и этот вызывающий также не избавлен ... т.е. ему await+asyncтоже нужно то же самое. И цепь продолжается. Но всегда будет Taskодин конец.

Все хорошо, но один разработчик был удивлен, увидев, что # 1 (Задача) отсутствует ...

Поделитесь смущением разработчика: 5 минут

Разработчик допустил ошибку, не реализовав, Taskно это все еще работает! Постарайтесь понять вопрос и просто принятый ответ, приведенный здесь . Надеюсь, что вы прочитали и полностью поняли. Суть в том, что мы можем не видеть / реализовывать «Задачу», но она реализована где-то в родительском классе. Аналогично, в нашем примере вызов уже созданного MethodAsync()способа проще, чем реализация этого метода с помощью Task( MethodTask()). Большинству разработчиков трудно передумать Tasksпри преобразовании кода в асинхронный.

Совет: попробуйте найти существующую реализацию Async (например, MethodAsyncили ToListAsync), чтобы передать сложность на аутсорсинг. Таким образом, нам нужно иметь дело только с Async и await (что легко и очень похоже на обычный код)

Проблема: быстро изменить реальную реализацию нормального кода на асинхронную работу: 2 минуты

Строка кода, показанная ниже в Data Layer, начала ломаться (много мест). Потому что мы обновили часть нашего кода с .Net Framework 4.2. * До ядра .Net. Мы должны были исправить это за 1 час по всему приложению!

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

очень просто!

  1. Мы установили пакет nuget EntityFramework, потому что он имеет QueryableExtensions. Или, другими словами, это делает реализацию (задачу) Async, чтобы мы могли выжить с простыми Asyncиawait в коде.
  2. namespace = 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 Core мы могли бы посмотреть замечательное короткое видео о преобразовании синхронных вызовов в асинхронные , возможно, именно в этом направлении можно было бы двигаться после прочтения этого.


фантастический ответ! это мне очень помогло
cklimowski

1
Хороший ответ. Возможно, вы захотите исправить пару небольших вещей, таких как: (а) упоминание о .Net framework 4.2 (такой версии, о которой я знаю, не существует) (б) добавление в EntityFrameWork => EntityFramework
immitev

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
Спасибо! первый ответ, который на самом деле делает некоторую работу вместо ожидания.
Джеффнл

спасибо за показ task.Wait();и как его можно использовать, чтобы избежать асинхронного / ожидающего ада: P
кодировщик

12

Этот ответ призван предоставить некоторую информацию, относящуюся к ASP.NET.

Используя async / await в контроллере MVC, можно увеличить использование пула потоков и добиться гораздо лучшей пропускной способности, как объясняется в следующей статье:

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

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


12

Async & Await Simple Explanation

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

Человек может ждать своего утреннего поезда. Это все, что они делают, поскольку это их основная задача, которую они выполняют в настоящее время. (синхронное программирование (что вы обычно делаете!))

Другой человек может ждать своего утреннего поезда, пока он курит сигарету и затем пьет свой кофе. (Асинхронное программирование)

Что такое асинхронное программирование?

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

Что на самом деле делает ключевое слово async?

Префикс асинхронного ключевого слова к имени метода, например

async void DoSomething(){ . . .

позволяет программисту использовать ключевое слово await при вызове асинхронных задач. Это все, что он делает.

Почему это важно?

Во многих программных системах основной поток зарезервирован для операций, конкретно относящихся к пользовательскому интерфейсу. Если на моем компьютере запущен очень сложный рекурсивный алгоритм, выполнение которого занимает 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() {..

Не стесняйтесь просматривать исходный код класса Task здесь .


4
Спасибо, что нашли время написать этот.
Прашант

10

Async / Await

На самом деле 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]
        });
    }

Код выше имеет несколько недостатков. Ошибки не передаются и их трудно прочитать. Но Async и Await приходят, чтобы помочь нам:

    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 и Await используются с асинхронными вызовами, чтобы этого не делать. Для этого вы должны использовать Task Libary , например 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;
    }

На самом деле вы можете вызвать асинхронный метод без ключевого слова await, но это означает, что любое исключение здесь проглатывается в режиме выпуска:

    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 Docs для большего количества примеров.


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 (), async и await. операторов в одной и той же программе.

Эта скрипка должна очистить вашу концепцию цикла выполнения.

Вот пример кода

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 не запускает метод в отдельном потоке. Начальный асинхронный метод выполняется синхронно до тех пор, пока не достигнет ожидающего выполнения задачи, занимающей много времени.

2) Вы можете ожидать метода, который возвращает Task или Task типа T. Вы не можете ожидать метода async 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 #) объясняет это явно:

Ключевые слова async и await не приводят к созданию дополнительных потоков. Асинхронные методы не требуют многопоточности, потому что асинхронный метод не выполняется в своем собственном потоке. Метод выполняется в текущем контексте синхронизации и использует время в потоке, только когда метод активен.


1

В следующем коде метод HttpClient GetByteArrayAsync возвращает задачу getContentsTask. Задача - это обещание создать фактический байтовый массив, когда задача будет выполнена. Оператор await применяется к getContentsTask для приостановки выполнения в SumPageSizesAsync до завершения getContentsTask. Тем временем управление возвращается вызывающей стороне SumPageSizesAsync. Когда getContentsTask завершается, выражение await преобразуется в байтовый массив.

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

Ответы здесь полезны в качестве общего руководства по await / async. Они также содержат некоторые подробности о том, как await / async подключен. Я хотел бы поделиться с вами некоторым практическим опытом, который вы должны знать, прежде чем использовать этот шаблон проектирования.

Термин «ожидание» является буквальным, поэтому любой поток, к которому вы его вызываете, будет ожидать результата метода, прежде чем продолжить. На переднем плане , это катастрофа . Поток переднего плана несет бремя создания вашего приложения, включая представления, модели представлений, начальные анимации и все остальное, что вы загрузили с этими элементами. Поэтому, когда вы ожидаете потока переднего плана, вы останавливаете приложение. Пользователь ждет и ждет, когда ничего не происходит. Это обеспечивает отрицательный пользовательский опыт.

Вы, конечно, можете ожидать фоновый поток, используя различные средства:

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должны быть использованы только для CPU связанного кода 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, он устанавливает остальную часть метода как продолжение, которое будет выполнено, когда все, что вы ожидаете, будет завершено. Он выходит из метода, в котором вы его использовали, чтобы вызывающий мог продолжить. Затем, когда ожидаемая строка фактически завершена, она завершает остаток этого метода в каком-то потоке (обычно рабочем потоке).
Дон Чидл

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