警告 未等待此调用,将继续执行当前方法
本文关键字:执行 继续 方法 等待 调用 警告 | 更新日期: 2023-09-27 17:59:30
刚刚得到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
不是异步的,则此方法需要在回调中嵌套回调。使用异步,我可以用这种非常自然的方式编写我的方法:
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();
}
但这是更多多余的代码。所以我必须编写我不需要的代码,或者容忍不必要的警告。
我对异步的使用有什么问题吗?
如果你真的不需要结果,你可以简单地更改GetNameAsync
的签名来返回void
:
public static async void GetNameAsync()
{
...
}
考虑查看相关问题的答案:返回无效和返回任务有什么区别?
更新
如果需要结果,可以更改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
预处理器指令。我在这里和那里有一些异步代码,我明确不想在某些情况下等待,我不喜欢警告和未使用的变量,就像你们其他人一样:
#pragma warning disable 4014
SomeMethodAsync();
#pragma warning restore 4014
"4014"
来自此 MSDN 页:编译器警告(级别 1(CS4014。
另请参阅@ryan-horath的警告/答案 这里 https://stackoverflow.com/a/12145047/928483.
在异步调用期间引发的未等待的异常将丢失。若要消除此警告,应将异步调用的 Task 返回值分配给变量。这可确保你有权访问引发的任何异常,这些异常将在返回值中指示。
C# 7.0 更新
C# 7.0 添加了一项新功能,丢弃变量:丢弃 - C# 指南,在这方面也可以提供帮助。
_ = SomeMethodAsync();
我不是特别喜欢将任务分配给未使用的变量或更改方法签名以返回 void 的解决方案。 前者创建多余的、非直观的代码,而后者可能是不可能的,如果你正在实现一个接口,或者你想使用返回的 Task 的函数。
我的解决方案是创建Task的扩展方法,称为DoNotAwait((,它什么都不做。 这不仅会抑制所有警告,无论是 ReSharp 还是其他警告,而且会使代码更易于理解,并向代码的未来维护者表明您真正希望不等待调用。
扩展方法:
public static class TaskExtensions
{
public static void DoNotAwait(this Task task) { }
}
用法:
public static void DoStuff()
{
GetNameAsync().DoNotAwait();
MainWorkOfApplicationIDontWantBlocked();
}
编辑补充:这类似于Jonathan Allen的解决方案,如果尚未启动,扩展方法将启动任务,但我更喜欢使用单一用途函数,以便调用者的意图完全清楚。
async 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
方法必须与UI甚至任何UI绑定的交互(WINRT/MVVM,我在看你(,那么它会变得更有趣=(
您需要像这样将引用传递给 UI 调度程序...
Task.Run(async () => await GetNameAsync(CoreApplication.MainView.CoreWindow.Dispatcher));
然后在异步方法中,您需要与 UI 或 UI 绑定元素交互,认为调度程序...
dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { this.UserName = userName; });
这是我目前正在做的事情:
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/
根据关于此警告的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 中出现"从未使用局部变量"消息。
这里有一个简单的解决方案。
public static class TasksExtensions
{
public static void RunAndForget(this Task task)
{
}
}
问候
更改方法签名以返回void
(因为返回void
应始终是无效的 ed(,则可以像这样使用 C# 7.0+ 丢弃功能,这比分配给变量略好(并且应该删除大多数其他源验证工具警告(:
public static void DoStuff()
{
_ = GetNameAsync(); // we don't need the return value (suppresses warning)
MainWorkOfApplicationIDontWantBlocked();
}
导致超浮代码的简化示例。通常,您希望使用在程序中的某个点从阻塞源获取的数据,因此您希望返回结果,以便可以访问数据。
如果你真的有一些事情与程序的其余部分完全隔离,异步将不是正确的方法。只需为该任务启动一个新线程即可。
你真的要忽略结果吗? 就像包括忽略任何意外异常一样?
如果没有,你可能想看看这个问题:即发即弃的方法,
Task tResult = GetNameAsync((;
这不会产生任何错误,也无需使调用者方法异步。