Только что получил VS2012 и пытаюсь разобраться async
.
Допустим, у меня есть метод, который получает какое-то значение из источника блокировки. Я не хочу, чтобы вызывающий метод блокировал. Я мог бы написать метод для приема обратного вызова, который вызывается при поступлении значения, но поскольку я использую C # 5, я решил сделать метод асинхронным, чтобы вызывающим абонентам не приходилось иметь дело с обратными вызовами:
// contrived example (edited in response to Servy's 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 и позволить 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 в метод, который не является асинхронным:
public static Task GetNameWrapper()
{
return GetNameAsync();
}
Но это еще более лишний код. Поэтому мне приходится писать код, который мне не нужен, или терпеть ненужные предупреждения.
Есть ли что-то неправильное в моем использовании async?
источник
PromptForStringAsync
вы делаете больше работы, чем вам нужно; просто верните результатTask.Factory.StartNew
. Это уже задача, значение которой - строка, введенная в консоли. Не нужно ждать возврата результата; это не добавляет никакой новой ценности.GetNameAsync
предоставить полное имя, которое было предоставлено пользователем (т.е.Task<Name>
вместо того, чтобы просто возвращатьTask
?),DoStuff
Можно было бы сохранить эту задачу, и либоawait
ее после другого метода, либо даже передать задачу этому другому метод, чтобы он могawait
илиWait
где-то внутри его реализации.async
ключевое слово.Ответы:
Если вам действительно не нужен результат, вы можете просто изменить
GetNameAsync
подпись, чтобы вернутьvoid
:Попробуйте увидеть ответ на связанный вопрос: в чем разница между возвратом void и возвратом задачи?
Обновить
Если вам нужен результат, вы можете изменить его
GetNameAsync
на return, напримерTask<string>
:И используйте его следующим образом:
источник
GetNameAsync
не возвращает никакого значения (кроме самого результата, конечно).void
, он не знает, когда это будет сделано. Именно это я имел в виду, когда сказал «результат» в своем предыдущем комментарии.async void
методов, кроме обработчиков событий.async void
любое исключение, которое вы не поймаете, приведет к сбою вашего процесса, но в .net 4.5 он будет продолжать работать.Я довольно поздно к этому обсуждению, но есть также возможность использовать
#pragma
директиву препроцессора. У меня тут и там есть асинхронный код, который я явно не хочу ждать в некоторых условиях, и мне не нравятся предупреждения и неиспользуемые переменные, как и всем остальным:Это
"4014"
происходит с этой страницы MSDN: Предупреждение компилятора (уровень 1) CS4014 .См. Также предупреждение / ответ @ ryan-horath здесь https://stackoverflow.com/a/12145047/928483 .
Обновление для C # 7.0
В C # 7.0 добавлена новая функция сброса переменных: Discards - Руководство по C # , которая также может помочь в этом отношении.
источник
var
, просто напишите_ = SomeMethodAsync();
Мне не особо нравятся решения, которые либо назначают задачу неиспользуемой переменной, либо меняют сигнатуру метода, чтобы вернуть void. Первый создает излишний, не интуитивно понятный код, а второй может быть невозможен, если вы реализуете интерфейс или используете другую функцию, в которой вы хотите использовать возвращенную задачу.
Мое решение - создать метод расширения Task под названием DoNotAwait (), который ничего не делает. Это не только подавит все предупреждения, ReSharper или другие, но и сделает код более понятным и укажет будущим сопровождающим вашего кода, что вы действительно намеревались сделать так, чтобы вызов не ожидался.
Метод расширения:
Использование:
Отредактировано для добавления: это похоже на решение Джонатана Аллена, в котором метод расширения запускает задачу, если она еще не запущена, но я предпочитаю иметь одноцелевые функции, чтобы намерение вызывающего абонента было полностью ясным.
источник
async void
ПЛОХО!Я предлагаю вам явно запустить
Task
через анонимный метод ...например
Или, если вы хотите, чтобы он заблокировался, вы можете дождаться анонимного метода
Однако, если ваш
GetNameAsync
метод должен взаимодействовать с пользовательским интерфейсом или даже с чем-либо связанным с пользовательским интерфейсом (WINRT / MVVM, я смотрю на вас), тогда он становится немного смешнее =)Вам нужно будет передать ссылку диспетчеру пользовательского интерфейса следующим образом ...
И затем в вашем асинхронном методе вам нужно будет взаимодействовать с вашим пользовательским интерфейсом или элементами, связанными с пользовательским интерфейсом, подумал, что диспетчер ...
источник
This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
это также приводит к созданию нового потока, тогда как новый поток не обязательно будет создан только с помощью async / await.Вот чем я сейчас занимаюсь:
Где
RunConcurrently
определяется как ...https://github.com/docevaad/Anchor/blob/master/Tortuga.Anchor/Tortuga.Anchor.source/shared/TaskUtilities.cs
https://www.nuget.org/packages/Tortuga.Anchor/
источник
public static void Forget(this Task task) { }
async Task
. Некоторые задачи нужно запускать вручную.Согласно статье Microsoft об этом предупреждении, вы можете решить эту проблему, просто назначив возвращенную задачу переменной. Ниже приведен перевод кода, представленного в примере Microsoft:
Обратите внимание, что это приведет к появлению сообщения «Локальная переменная никогда не используется» в ReSharper.
источник
Task
-возврат функций следует использовать,await
если у вас нет веской причины не делать этого. Здесь нет причин, по которым отказ от задачи был бы лучше, чем уже принятый ответ об использованииasync void
метода.async void
создает серьезные проблемы с обработкой ошибок и приводит к непроверяемому коду (см. мою статью MSDN ). Было бы гораздо лучше использовать переменную - если вы абсолютно уверены, что хотите, чтобы исключения проглатывались молча. Скорее всего,Task
оператор захочет начать две с, а затем выполнитьawait Task.WhenAll
.async void DoNotWait(Task t) { await t; }
вспомогательный метод, чтобы избежать недостатковasync void
описываемых вами методов. (И я не думаюTask.WhenAll
, что ОП хочет этого, но вполне может быть.)Вот простое решение.
С уважением
источник
Это ваш упрощенный пример, который вызывает избыточный код. Обычно вы хотели бы использовать данные, которые были извлечены из источника блокировки в какой-то момент программы, поэтому вам нужно вернуть результат, чтобы можно было добраться до данных.
Если у вас действительно есть что-то, что происходит полностью изолированно от остальной программы, асинхронный подход не будет правильным. Просто начните новый поток для этой задачи.
источник
async
было разработано для очистки ( например )MethodWithCallback((result1) => { Use(result1); MethodWithCallback((result2) => { Use(result1,result2); })
даже в этом тривиальном примере разбирать сложно. С помощью async, когда я пишу, для меня генерируется эквивалентный код,result1 = await AsyncMethod(); Use(result1); result2 = await AsyncMethod(); Use(result1,result2);
который намного легче читать (хотя ни один из них не очень удобочитаемый, разбитый вместе в этом комментарии!)Use
.Вы действительно хотите проигнорировать результат? как в том числе игнорировать любые неожиданные исключения?
Если нет, вы можете взглянуть на этот вопрос: подход «огонь и забыть» ,
источник
Если вы не хотите изменять сигнатуру метода для возврата
void
(поскольку возвратvoid
всегда должен быть недействительным ), вы можете использовать такую функцию C # 7.0+ Discard , которая немного лучше, чем присвоение переменной (и должна удалить большинство других предупреждения средств проверки источника):источник