Предупреждение этого вызова не ожидается, выполнение текущего метода продолжается
Просто получил VS2012 и попытался получить дескриптор на async
.
Скажем, у меня есть метод, который извлекает некоторое значение из источника блокировки. Я не хочу, чтобы вызывающий метод блокировал. Я мог бы написать метод для выполнения обратного вызова, который вызывается при достижении значения, но поскольку я использую С# 5, я решил сделать метод async, чтобы вызывающие абоненты не имели дело с обратными вызовами:
// contrived example (edited in response to Servy comment)
public static Task<string> PromptForStringAsync(string prompt)
{
return Task.Factory.StartNew(() => {
Console.Write(prompt);
return Console.ReadLine();
});
}
Вот пример метода, который его вызывает. Если PromptForStringAsync
не был асинхронным, этот метод потребовал бы вставки обратного вызова в обратном вызове. С async я могу написать свой метод самым естественным образом:
public static async Task GetNameAsync()
{
string firstname = await PromptForStringAsync("Enter your first name: ");
Console.WriteLine("Welcome {0}.", firstname);
string lastname = await PromptForStringAsync("Enter your last name: ");
Console.WriteLine("Name saved as '{0} {1}'.", firstname, lastname);
}
Пока все хорошо. Проблема заключается в том, что я вызываю GetNameAsync:
public static void DoStuff()
{
GetNameAsync();
MainWorkOfApplicationIDontWantBlocked();
}
Вся точка GetNameAsync
заключается в том, что она асинхронна. Я не хочу, чтобы он блокировался, потому что я хочу вернуться к MainWorkOfApplicationIDontWantBlocked ASAP и позволить GetNameAsync делать свое дело в фоновом режиме. Тем не менее, вызов этого метода дает мне предупреждение компилятора в строке GetNameAsync
:
Warning 1 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
Я прекрасно понимаю, что "выполнение текущего метода продолжается до завершения вызова". Что точка асинхронного кода, правильно?
Я предпочитаю, чтобы мой код компилировался без предупреждений, но здесь нечего "исправлять", потому что код выполняет именно то, что я намереваюсь сделать. Я могу избавиться от предупреждения, сохранив возвращаемое значение GetNameAsync
:
public static void DoStuff()
{
var result = GetNameAsync(); // supress warning
MainWorkOfApplicationIDontWantBlocked();
}
Но теперь у меня есть лишний код. Visual Studio, похоже, понимает, что я был вынужден написать этот ненужный код, потому что он подавляет обычное предупреждение "значение никогда не используется".
Я также могу избавиться от предупреждения, обернув GetNameAsync в методе, который не async:
public static Task GetNameWrapper()
{
return GetNameAsync();
}
Но это еще более лишний код. Поэтому мне нужно написать код, который мне не нужен, или терпеть ненужное предупреждение.
Есть ли что-то в моем использовании async, что здесь неправильно?
Ответы
Ответ 1
Если вам действительно не нужен результат, вы можете просто изменить подпись GetNameAsync
для возврата void
:
public static async void GetNameAsync()
{
...
}
Рассмотрите возможность ответа на соответствующий вопрос:
Какая разница между возвратом void и возвратом задачи?
Обновление
Если вам нужен результат, вы можете изменить GetNameAsync
, чтобы вернуть, скажем, Task<string>
:
public static async Task<string> GetNameAsync()
{
string firstname = await PromptForStringAsync("Enter your first name: ");
string lastname = await PromptForStringAsync("Enter your last name: ");
return firstname + lastname;
}
И используйте его следующим образом:
public static void DoStuff()
{
Task<string> task = GetNameAsync();
// Set up a continuation BEFORE MainWorkOfApplicationIDontWantBlocked
Task anotherTask = task.ContinueWith(r => {
Console.WriteLine(r.Result);
});
MainWorkOfApplicationIDontWantBlocked();
// OR wait for the result AFTER
string result = task.Result;
}
Ответ 2
Я довольно поздно к этому обсуждению, но есть также возможность использовать директиву препроцессора #pragma
. У меня есть некоторый асинхронный код здесь и там, который я явно не хочу ждать в некоторых условиях, и мне не нравятся предупреждения и неиспользуемые переменные, как и все остальные:
#pragma warning disable 4014
SomeMethodAsync();
#pragma warning restore 4014
"4014"
исходит от этой страницы MSDN: Предупреждение компилятора (уровень 1) CS4014.
Смотрите также предупреждение/ответ @ryan-horath здесь fooobar.com/questions/70783/....
Исключения, выданные во время не ожидаемого асинхронного вызова, будут потеряны. Чтобы избавиться от этого предупреждения, вы должны присвоить переменной значение, возвращаемое заданием асинхронного вызова. Это гарантирует, что у вас есть доступ к любым исключениям, которые будут указаны в возвращаемом значении.
Обновление для С# 7.0
В С# 7.0 добавлена новая функция отмены переменных: Discards - С# Guide, которая также может помочь в этом отношении.
var _ = SomeMethodAsync();
Ответ 3
Я не особенно люблю решения, которые либо назначают задачу неиспользуемой переменной, либо меняют подпись метода для возврата void. Первый создает лишний, неинтуитивный код, в то время как последний может быть невозможен, если вы реализуете интерфейс или используете другое использование функции, в которой вы хотите использовать возвращенную задачу.
Мое решение - создать метод расширения задачи, называемый DoNotAwait(), который ничего не делает. Это будет не только подавлять все предупреждения, ReSharper или иначе, но делает код более понятным и указывает будущим сопровождающим вашего кода, который вы действительно предназначили для того, чтобы вызов не ожидался.
Метод расширения:
public static class TaskExtensions
{
public static void DoNotAwait(this Task task) { }
}
Использование:
public static void DoStuff()
{
GetNameAsync().DoNotAwait();
MainWorkOfApplicationIDontWantBlocked();
}
Отредактировано для добавления: это похоже на решение Джонатана Аллена, в котором метод расширения запускает задачу, если она еще не запущена, но я предпочитаю использовать одноцелевые функции, чтобы умывка вызывающего абонента была полностью понятной.
Ответ 4
async void
ПЛОХО!
- В чем разница между возвратом void и возвратом Задачи?
- https://jaylee.org/archive/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.html
Я предлагаю вам явно запустить Task
анонимным методом...
например,
public static void DoStuff()
{
Task.Run(async () => GetNameAsync());
MainWorkOfApplicationIDontWantBlocked();
}
Или, если вы хотите, чтобы он блокировался, вы можете дождаться анонимного метода
public static void DoStuff()
{
Task.Run(async () => await GetNameAsync());
MainWorkOfApplicationThatWillBeBlocked();
}
Однако, если ваш метод GetNameAsync
должен взаимодействовать с пользовательским интерфейсом или даже с чем-либо связанным с ним (WINRT/MVVM, я смотрю на вас), то он становится немного веселее =)
Вам нужно будет передать ссылку диспетчеру пользовательского интерфейса следующим образом...
Task.Run(async () => await GetNameAsync(CoreApplication.MainView.CoreWindow.Dispatcher));
И затем в вашем асинхронном методе вам нужно будет взаимодействовать с вашим пользовательским или связанным с ним элементами, считая, что диспетчер...
dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { this.UserName = userName; });
Ответ 5
Вот что я сейчас делаю:
SomeAyncFunction().RunConcurrently();
Где RunConcurrently
определяется как...
/// <summary>
/// Runs the Task in a concurrent thread without waiting for it to complete. This will start the task if it is not already running.
/// </summary>
/// <param name="task">The task to run.</param>
/// <remarks>This is usually used to avoid warning messages about not waiting for the task to complete.</remarks>
public static void RunConcurrently(this Task task)
{
if (task == null)
throw new ArgumentNullException("task", "task is null.");
if (task.Status == TaskStatus.Created)
task.Start();
}
https://github.com/docevaad/Anchor/blob/master/Tortuga.Anchor/Tortuga.Anchor.source/shared/TaskUtilities.cs
https://www.nuget.org/packages/Tortuga.Anchor/
Ответ 6
Согласно статье Microsoft об этом предупреждении, вы можете решить ее, просто назначив возвращаемую задачу переменной. Ниже приведен перевод кода, приведенного в примере Microsoft:
// To suppress the warning without awaiting, you can assign the
// returned task to a variable. The assignment doesn't change how
// the program runs. However, the recommended practice is always to
// await a call to an async method.
// Replace Call #1 with the following line.
Task delayTask = CalledMethodAsync(delay);
Обратите внимание, что выполнение этого приведет к появлению сообщения "Локальная переменная никогда не используется" в ReSharper.
Ответ 7
Здесь простое решение.
public static class TasksExtensions
{
public static void RunAndForget(this Task task)
{
}
}
Привет
Ответ 8
Это ваш упрощенный пример, который вызывает сверхплотный код. Обычно вы хотели бы использовать данные, которые были извлечены из источника блокировки в какой-то момент в программе, поэтому вы хотите вернуть результат, чтобы можно было получить данные.
Если у вас действительно есть что-то, что полностью изолировано от остальной части программы, async не будет правильным подходом. Просто запустите новый поток для этой задачи.
Ответ 9
Вы действительно хотите проигнорировать результат? как в том числе игнорировать любые неожиданные исключения?
Если нет, вы можете взглянуть на этот вопрос: Пожар и Забытый подход,
Ответ 10
Если вы не хотите изменять сигнатуру метода для возврата void
(поскольку всегда следует избегать возврата void
), вы можете использовать С# 7. 0+ Отменить функцию, например, немного лучше, чем присвоение переменной (и следует удалить предупреждения большинства других инструментов проверки исходного кода):
public static void DoStuff()
{
_ = GetNameAsync(); // we don't need the return value (suppresses warning)
MainWorkOfApplicationIDontWantBlocked();
}