Из того, что я понял во всех уроках async/await в С#, я понял, что когда мы определяем асинхронный метод, это означает, что мы можем добавить в него ключевое слово await с задачей в качестве параметра, чтобы метод возвращался к вызывающей стороне, если задача слишком длинная Затем метод может возобновиться после завершения задачи.
public void MyMainMethod()
{
DoSomethingAsync();
KeepOnDoingOtherStuff();
}
public async Task DoSomethingAsync()
{
//Do whatever
await LongOperation();
// Rest of the method that is executed when the long operation is
//finished.
}
Таким образом, здесь, пока выполняется длинная операция, как и ожидается, асинхронный DoSomething передает руку MainMethod и выполняется KeepOnDoingOtherStuff. Хорошо
Теперь моя проблема в том, что мой DoSomethingAsync сам по себе ожидается. В этом случае:
public async void MyMainMethod()
{
await DoSomethingAsync();
//Here is a critical method that I want to execute only when
//DoSomethingAsync is finished
CriticalStuff();
}
public async Task DoSomethingAsync()
{
//Do whatever
await LongOperation();
// Rest of the method that is executed when the long operation is
//finished.
}
Теперь я жду своего DoSomethingAsync, так что я уверен, что он не идет дальше, но проблема в том, что DoSomethingAsync() все еще дает руку вызывающей стороне, когда она ожидает LongOperation(), и мой метод Main возобновляется, что я и делаю не хочу, потому что он выполняет мою критическую вещь.
В асинхронной логике я кое-что упускаю. Какой смысл "ждать" метода, в котором есть асинхронные методы?
Я был бы очень признателен за решение моей проблемы.
Заранее спасибо.
Я думаю, что несоответствие здесь происходит от этих слов:
заставить метод вернуться к вызывающей стороне, если задача слишком длинная.
await
не долго, точно. Это про асинхронный. Если что-то занимает много времени, но происходит на локальном процессоре, тогда точно: это не совсем подходит для await
(хотя вы можете использовать механизм await
в качестве удобного способа передачи данных в рабочий поток, а затем вернуться к синхронизации контекстный поток, когда он завершается).
Лучший пример, тем не менее, это когда задача внешняя; это может быть вызов базы данных SQL, или redis, или через http, или другому процессу через некоторый уровень ввода-вывода, или ожидание данных сокета. Обратите внимание, что даже общение с физически локальным хранилищем SSD может потребовать длительного ожидания, по крайней мере, измеряемого скоростью процессора. Когда локальный поток ничего не делает, кроме блокирования (ожидания ответа), тогда: во многих случаях есть гораздо лучшие вещи, которые поток может делать.
В случае клиентского приложения эти "лучшие вещи" включают в себя рисование пользовательского интерфейса и реагирование на ввод. В случае серверного приложения эти "лучшие вещи" включают обслуживание других одновременных запросов/загрузки.
При измерении таким образом: await
действует как эффективный способ написания кода, который нуждается в данных из внешних источников, но не хочет просто блокировать текущий поток, пока эти данные поступают. Если это не ваш сценарий: await
может быть не для вас.
async
для координации 2 (или более) действий, и: это совершенно верно. Просто ... не await
пока вы не будете готовы "присоединиться" (в терминах потоков)
Я обсуждаю этот вариант вашего кода:
public async void MyMainMethod()
{
await DoSomethingAsync();
//Here is a critical method that I want to execute only when
//DoSomethingAsync is finished
CriticalStuff();
}
public async Task DoSomethingAsync()
{
//Do whatever
await LongOperation();
// Rest of the method that is executed when the long operation is
//finished.
}
Когда DoSomethingAsync
возвращается, он не просто слепо передает управление обратно вызывающему методу. Это вручает это Task
. Если метод на самом деле завершен синхронно, он вернет задачу, которая уже выполнена. В противном случае, если у метода все еще есть работа, Task
будет запущена, но еще не завершена.
Итак, DoSomethingAsync
фактически запускает некоторое асинхронное поведение и возвращает обратно невыполненную Task
в Main
. Что Main
делает с этим? await
этого. await
- это то, как этот метод указывает: "Я не могу сделать ничего полезного, если это ожидаемое (обычно Task
) не выполнено".
Если (как здесь), Task
еще не выполнена, она не продвигается дальше (сама), пока это не произойдет. Если это первое (обязательное) await
в методе, именно в этот момент запускается асинхронный механизм, регистрируется продолжение и возвращается из самого Main
.
Он не перешагнет ни одной строки await
до тех пор, пока Task
(или другая ожидаемая), которую вы предоставите в качестве аргумента await
будет завершена, а для методов, использующих механизм async
, - до тех пор, пока этот метод не будет полностью выполнен.
(Я проигнорировал историю исключений в приведенном выше описании. Все это счастливые вещи)
await
и на самом деле приходится ждать, то Task
, которая представляет завершение этого вызова метода по - прежнему отмечена как бег, не завершена. Ваш метод не считается завершенным, пока эта Task
перейдет к Complete
. И это та Task
которую ваш абонент, в свою очередь, может await
. Есть яснее?
CriticalStuff()
не должен вызываться до тех пор, покаDoSomethingAsync()
будет полностью завершен. В коде примера, который вы разместили здесь, должно быть что-то не хватает.