Как выполнить асинхронную операцию с возвращаемым результатом фоном (то есть проигнорировав результат) в C#

95
07 апреля 2021, 15:40

У меня есть асинхронная функция, она возвращает результат. Иногда он мне нужен, иногда нет. В описанном ниже случае, как раз нет. Вот код:

var ignore = _applicationRepo.UpdateAsync(application);

если написать

_applicationRepo.UpdateAsync(application);

тогда IDE пишет варнинг warning this call is not awaited, execution of the current method continues before the call complited.

То есть, пока вызвавший метод выполняется, выполняется и мой UpdateAsync? А как только вызвавший завершится, UpdateAsync может тоже завершиться, так сказать, на полуслове, не выполнившись? Верно? Или как это понимать?

И самый главный вопрос, я могу просто проигнорировать результат, не создавая "игнор" переменные и в то же время, быть уверенным, что метод выполнится фоном, даже если вызывающий уже отработал? Есть ли какой-нибудь красивый способ это сделать?

Answer 1

Начиная с C# 7.0 в языке появилась такая возможность, как Discards.

_ = _applicationRepo.UpdateAsync(application);

Используя знак подчёркивания мы явно даём понять, что результат не используется. И память под него не выделяется.

Answer 2

То есть, пока вызвавший метод выполняется, выполняется и мой UpdateAsync? А как только вызвавший завершится, UpdateAsync может тоже завершиться, так сказать, на полуслове, не выполнившись? Верно? Или как это понимать?

Нет, .NET среда не может просто так завершить любой метод, ведь это как минимум небезопасно. Как только вы вызываете UpdateAsync объект Task добавляется в пул, и остается там до того времени, пока не выполнится до конца.

Красивого способа как игнорировать результат нет. Можете отключить предупреждение компилятора:

#pragma warning disable 4014
    _applicationRepo.UpdateAsync();
#pragma warning restore 4014

Ну или если у вас таких вызовов много, тогда можно один раз прописать в файле #pragma warning disable 4014 и не возобновлять его.

Answer 3

Судя по комментариям, вы не совсем верно представляете себе, как работает async-await. Он не запускает метод в фоне, как вы ожидаете. Он просто прерывает выполнение на время асинхронной операции.

Например, у вас есть следующий код

async static Task<bool> SomeInnerMethodAsync()
{
    Console.WriteLine("Start of SomeInnerMethodAsync");
    // тут какая-то долгая сетевая или дисковая операция
    await Task.Delay(1000);
    Console.WriteLine("End of SomeInnerMethodAsync");
    return true;
}
async static Task<bool> UpdateAsync()
{
    Console.WriteLine("Start of UpdateAsync");
    bool result = await SomeInnerMethodAsync();
    Console.WriteLine("End of UpdateAsync");
    return result;
}
async static Task OuterMethod()
{
    Console.WriteLine("Start of OuterMethod");
    await UpdateAsync();
    Console.WriteLine("End of OuterMethod");
}

При выполнении произойдет следующее:

Start of OuterMethod
Start of UpdateAsync
Start of SomeInnerMethodAsync
<ожидание>
End of SomeInnerMethodAsync
End of UpdateAsync
End of OuterMethod

Т.е. выполнение дошло внутрь до вызова, который действительно не может завершиться синхронно (Task.Delay), и только тогда остановилось. По завершению Task.Delay выполнение продолжилось на потоке из пула.

Если вы уберете await, то вместо приостановки управление перейдет к остатку OuterMethod сразу же, а остатки внутренних методов будут выполнены на потоке из пулла:

async static Task OuterMethod()
{
    Console.WriteLine("Start of OuterMethod");
    UpdateAsync();
    Console.WriteLine("End of OuterMethod");
}

Вывод:

Start of OuterMethod
Start of UpdateAsync
Start of SomeInnerMethodAsync
End of OuterMethod, продолжение выполнения кода, вызвашего OuterMethod
<пауза, вывод из потока в пуле:>
End of SomeInnerMethodAsync
End of UpdateAsync

Если вам нужно именно запустить метод в фоне - используйте Task.Run(), а не async-await.

Answer 4

Сначала ответ на главный вопрос: await _applicationRepo.UpdateAsync(application); таким образом результат может быть проигнорирован. В C/C++/C# функция может возвращать значение, но мы отнюдь не обязаны его получать.

Теперь про остальное: вызывающий метод не завершится, пока не будут завершены все функции, предварённые оператором await в нём. Вызывающий метод отработает, но не вернёт значение, пока не дождётся всех await. (wait – ждать).

READ ALSO
Не удается верно указать индексы дублирующихся символов в строке

Не удается верно указать индексы дублирующихся символов в строке

Необходимо вывести индексы начала и конца последовательности дублирующихся гласных символов

85
Корзина покупок в ASP.NET Core

Корзина покупок в ASP.NET Core

Изучаю ASPNet Core и никак не могу найти, как правильно реализовать корзину с помощью этой технологии

87
C# запись в ACC, минуя Wav

C# запись в ACC, минуя Wav

Нужно каким то образом записать звук с микрофона, и сохранить это в формате ACCЕсть NAudio, но там сначала пишется в Wav, и только потом можно конвертнуть

98
Запрос с несколькими COUNT

Запрос с несколькими COUNT

Я не специалист по БДМой уровень это простые запросы

110