警告这个调用没有等待,继续执行当前的方法

刚刚得到VS2012,并试图获得async处理。

比方说,我有一个方法,从阻塞源获取一些值。 我不希望方法的调用者阻止。 我可以编写方法来获取一个callback,当值到达时被调用,但是由于我使用的是C#5,我决定使方法asynchronous,因此调用者不必处理callback:

 // 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不是asynchronous,则此方法需要在callback中嵌套callback。 通过asynchronous,我可以用这种非常自然的方式来编写我的方法:

 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在于它是asynchronous的。 我不希望它阻止,因为我想回到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. 

我完全知道“在调用完成之前继续执行当前的方法”。 这就是asynchronous代码的重点,对吧?

我更喜欢我的代码编译没有警告,但没有什么可以“修复”在这里,因为代码正在做我打算做的事情。 我可以通过存储GetNameAsync的返回值来摆脱警告:

 public static void DoStuff() { var result = GetNameAsync(); // supress warning MainWorkOfApplicationIDontWantBlocked(); } 

但是现在我有了多余的代码。 Visual Studio似乎明白,我被迫写这不必要的代码,因为它抑制了正常的“价值从未使用”的警告。

我也可以通过在不是asynchronous的方法中包装GetNameAsync来摆脱警告:

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

但是这更是多余的代码。 所以我必须写代码,我不需要或容忍一个不必要的警告。

有什么关于我的asynchronous使用这是错误的吗?

如果你真的不需要结果,你可以简单的改变GetNameAsync的签名来返回void

 public static async void GetNameAsync() { ... } 

考虑看看相关问题的答案: 返回void和返回一个Task有什么区别?

更新

如果您需要结果,可以将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; } 

我对这个讨论已经很晚了,但是也可以使用#pragma预处理器指令。 我在这里和那里有一些asynchronous代码,我明确地不想在某些条件下等待,我不喜欢像其他人一样的警告和未使用的variables:

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

"4014"来自此MSDN页面: 编译器警告(级别1)CS4014 。

另请参阅@ ryan-horath的警告/答案https://stackoverflow.com/a/12145047/928483

asynchronous调用期间抛出的exception将会丢失。 为了摆脱这个警告,你应该把asynchronous调用的任务返回值赋给一个variables。 这确保您可以访问任何抛出的exception,这将在返回值中指示。

asynchronous无效是坏的!

返回void和返回一个Task有什么区别? http://www.jaylee.org/post/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.aspx http://www.tonicodes.net/blog /为什么任您应该-几乎从不写空隙asynchronous方法/

我build议你明确地通过匿名方法运行任务…

 public static void DoStuff() { Task.Run(async () => GetNameAsync()); MainWorkOfApplicationIDontWantBlocked(); } 

或者,如果您确实希望阻止,则可以使用匿名方法等待

 public static void DoStuff() { Task.Run(async () => await GetNameAsync()); MainWorkOfApplicationThatWillBeBlocked(); } 

但是,如果你的“GetNameAsync”方法必须与UI交互,甚至任何UI绑定,(WINRT / MVVM,我在看你); 那么它有点funkier =)

您需要将引用传递给UI调度程序,如下所示…

 Task.Run(async () => await GetNameAsync(CoreApplication.MainView.CoreWindow.Dispatcher)); 

然后在你的asynchronous方法,你需要与你的用户界面或用户界面绑定的元素,认为调度员交互…

 dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { this.UserName = userName; }); 

我并不特别喜欢将任务分配给一个未使用的variables,或将方法签名更改为返回void的解决scheme。 前者创build多余的,非直观的代码,而后者可能是不可能的,如果你正在实现一个接口或有另一个使用函数的地方,你想使用返回的任务。

我的解决scheme是创buildTask的扩展方法,称为DoNotAwait(),它什么都不做。 这不仅会抑制所有的警告,ReSharper或其他,但使代码更容易理解,并指示您的代码的未来维护者,你真的打算为这个电话不等待。

扩展方法:

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

用法:

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

编辑补充:这与Jonathan Allen的解决scheme相似,即如果尚未开始,扩展方法将启动任务,但我更喜欢使用单一function的函数,以使调用者的意图完全清晰。

这是我目前正在做的事情:

 SomeAyncFunction().RunConcurrently(); 

RunAsync被定义为…

  /// <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/

根据Microsoft有关此警告的文章,只需将返回的任务分配给一个variables即可解决此问题。 以下是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中的“Local variable is never used”消息。

这是你的简单例子,导致超级代码。 通常情况下,您会希望使用在程序中的某个位置从阻止源获取的数据,所以您希望返回结果以便能够访问数据。

如果真的发生了与程序其余部分完全隔离的事情,那么asynchronous并不是正确的方法。 只需为该任务启动一个新线程。

你真的想忽略结果吗? 在包括忽略任何意外的例外?

如果没有,你可能想看看这个问题: 火和忘记方法 ,

这里有一个简单的解决scheme

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

问候