Что происходит, когда вы ожидаете асинхронную задачу в C #?

2

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

В асинхронной логике я кое-что упускаю. Какой смысл "ждать" метода, в котором есть асинхронные методы?

Я был бы очень признателен за решение моей проблемы.

Заранее спасибо.

  • 2
    Во втором примере CriticalStuff() не должен вызываться до тех пор, пока DoSomethingAsync() будет полностью завершен. В коде примера, который вы разместили здесь, должно быть что-то не хватает.
  • 0
    Это хорошая статья об асинхронном программировании, если вам интересно больше узнать об этой теме. rubikscode.net/2018/05/28/...
Показать ещё 4 комментария
Теги:
asynchronous

2 ответа

6

Я думаю, что несоответствие здесь происходит от этих слов:

заставить метод вернуться к вызывающей стороне, если задача слишком длинная.

await не долго, точно. Это про асинхронный. Если что-то занимает много времени, но происходит на локальном процессоре, тогда точно: это не совсем подходит для await (хотя вы можете использовать механизм await в качестве удобного способа передачи данных в рабочий поток, а затем вернуться к синхронизации контекстный поток, когда он завершается).

Лучший пример, тем не менее, это когда задача внешняя; это может быть вызов базы данных SQL, или redis, или через http, или другому процессу через некоторый уровень ввода-вывода, или ожидание данных сокета. Обратите внимание, что даже общение с физически локальным хранилищем SSD может потребовать длительного ожидания, по крайней мере, измеряемого скоростью процессора. Когда локальный поток ничего не делает, кроме блокирования (ожидания ответа), тогда: во многих случаях есть гораздо лучшие вещи, которые поток может делать.

В случае клиентского приложения эти "лучшие вещи" включают в себя рисование пользовательского интерфейса и реагирование на ввод. В случае серверного приложения эти "лучшие вещи" включают обслуживание других одновременных запросов/загрузки.


При измерении таким образом: await действует как эффективный способ написания кода, который нуждается в данных из внешних источников, но не хочет просто блокировать текущий поток, пока эти данные поступают. Если это не ваш сценарий: await может быть не для вас.

  • 0
    Я понял но для меня смысл асинхронности DoSomethingAsync в том, что он может возвращаться к вызывающей стороне, пока он выполняет свою долгую операцию. Однако «вызывающая сторона» ожидает задачу DoSomethingAsync, поэтому она не будет ничего делать, пока она не завершена. Следовательно, я мог бы сделать DoSomethingAsync синхронным. Другими словами, почему любая Задача, которую мы ждем, будет асинхронной?
  • 0
    @Zeppelin вы говорите о параллелизме, а я говорю об асинхронности, связанной, но другой. Я в основном сосредотачиваюсь на асинхронности, и в этом случае ключевая вещь, которую вы пропускаете, это то, что потоки дороги , и что во многих сценариях есть несвязанные вещи, которые ваш поток мог бы с пользой делать, а не блокировать. С точки зрения того, что вы говорите о параллелизме: конечно, есть сценарии, в которых вы можете использовать async для координации 2 (или более) действий, и: это совершенно верно. Просто ... не await пока вы не будете готовы "присоединиться" (в терминах потоков)
Показать ещё 1 комментарий
1

Я обсуждаю этот вариант вашего кода:

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, - до тех пор, пока этот метод не будет полностью выполнен.

(Я проигнорировал историю исключений в приведенном выше описании. Все это счастливые вещи)

  • 0
    Поэтому, когда вы говорите, что Main «не может сделать ничего полезного, пока DoSomethingAsync не будет завершен»: когда мы можем сказать, что он официально завершен? потому что в этой задаче есть другая задача, которая выполняется и ожидает (ожидайте LongOperation ()). Если я применяю логику: в DoSomethingAsync я нажимаю ожидающую ожидаемую LongOperation () -> она еще не завершена, поэтому мы возвращаемся к вызывающей стороне -> который является Главным, а что после ожидают DoSomethingAsync () .... (это то, чего я конкретно не хочу, так как ничего еще не закончено ..). Отсюда и моя путаница ...
  • 1
    @Zeppelin - Постараюсь формулируя это по- другому - всякий раз , когда вы нажмете await и на самом деле приходится ждать, то Task , которая представляет завершение этого вызова метода по - прежнему отмечена как бег, не завершена. Ваш метод не считается завершенным, пока эта Task перейдет к Complete . И это та Task которую ваш абонент, в свою очередь, может await . Есть яснее?
Показать ещё 6 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню