it-swarm.com.ru

предупреждение об этом вызове не ожидается, выполнение текущего метода продолжается

Только что получил 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 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 в метод, который не является асинхронным:

    public static Task GetNameWrapper()
    {
        return GetNameAsync();
    }

Но это даже более лишний код. Поэтому я должен написать код, который мне не нужен, или терпеть ненужное предупреждение.

Есть ли что-то в моем использовании async, что здесь не так?

104
Mud

Если вам действительно не нужен результат, вы можете просто изменить подпись 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;
}
75
Nikolay Khil

Я довольно поздно к этому обсуждению, но есть также возможность использовать директиву препроцессора #pragma. У меня есть некоторый асинхронный код здесь и там, который я явно не хочу ждать в некоторых условиях, и мне не нравятся предупреждения и неиспользуемые переменные, как и все остальные:

#pragma warning disable 4014
SomeMethodAsync();
#pragma warning restore 4014

Код "4014" взят с этой страницы MSDN: Предупреждение компилятора (уровень 1) CS4014

Смотрите также предупреждение/ответ от @ ryan-horath здесь https://stackoverflow.com/a/12145047/928483 .

Исключения, выданные во время не ожидаемого асинхронного вызова, будут потеряны. Чтобы избавиться от этого предупреждения, вы должны присвоить переменной значение, возвращаемое заданием асинхронного вызова. Это гарантирует, что у вас есть доступ к любым исключениям, которые будут указаны в возвращаемом значении.

40
Willem Duncan

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

Мое решение состоит в том, чтобы создать метод расширения Task, называемый DoNotAwait (), который ничего не делает. Это не только подавит все предупреждения, ReSharper или иным образом, но сделает код более понятным и покажет будущим сопровождающим вашего кода, что вы действительно хотели, чтобы вызов не ожидался.

Метод расширения:

public static class TaskExtensions
{
    public static void DoNotAwait(this Task task) { }
}

Использование:

public static void DoStuff()
{
    GetNameAsync().DoNotAwait();
    MainWorkOfApplicationIDontWantBlocked();
}

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

25
Joe Savage

async void IS ПЛОХО!

Какая разница между возвратом void и возвратом Задачи?http://www.jaylee.org/post/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.aspxhttp://www.tonicodes.net/blog/why-you-should-almost-never-write-void-asynchronous-methods/

Что я предлагаю, так это то, что вы явно запускаете задачу через анонимный метод ...

то есть.

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; });
22
MrEs

Вот что я сейчас делаю:

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/

16
Jonathan Allen

Согласно статье 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
devlord

Это ваш упрощенный пример, который вызывает лишний код. Обычно вы хотите использовать данные, которые были извлечены из источника блокировки в какой-то момент в программе, поэтому вы хотите получить результат обратно, чтобы можно было получить доступ к данным.

Если у вас действительно есть что-то, что происходит полностью изолированным от остальной части программы, асинхронность не будет правильным подходом. Просто создайте новый поток для этой задачи.

1
Guffa

Вы действительно хотите игнорировать результат? как в том числе игнорировать любые неожиданные исключения? 

Если нет, возможно, вы захотите взглянуть на этот вопрос: Подход огня и забыть ,

0
Jens

Здесь простое решение.

public static class TasksExtensions
{
    public static void RunAndForget(this Task task)
    {
    }
}

С уважением

0
JuanluElGuerre