这不是"How to safely call an async method in C# without await"的重复。
如何很好地抑制以下警告?
警告CS4014:由于未等待此呼叫,因此在呼叫完成之前将继续执行当前方法。考虑将'await'运算符应用于调用的结果。
一个简单的例子:
static async Task WorkAsync()
{
await Task.Delay(1000);
Console.WriteLine("Done!");
}
static async Task StartWorkAsync()
{
WorkAsync(); // I want fire-and-forget
// more unrelated async/await stuff here, e.g.:
// ...
await Task.Delay(2000);
}
我尝试过但不喜欢的内容:
static async Task StartWorkAsync()
{
#pragma warning disable 4014
WorkAsync(); // I want fire-and-forget here
#pragma warning restore 4014
// ...
}
static async Task StartWorkAsync()
{
var ignoreMe = WorkAsync(); // I want fire-and-forget here
// ...
}
已更新,因为original accepted answer已被修改,我已将接受的答案更改为the one using C# 7.0 discards,因为我认为ContinueWith
不合适这里。每当我需要记录“即发即弃”操作的异常时,我都会使用更精细的方法proposed by Stephen Cleary here。
答案 0 :(得分:118)
您可以创建一个阻止警告的扩展方法。扩展方法可以为空,也可以在那里添加.ContinueWith()
的异常处理。
static class TaskExtensions
{
public static void Forget(this Task task)
{
task.ContinueWith(
t => { WriteLog(t.Exception); },
TaskContinuationOptions.OnlyOnFaulted);
}
}
public async Task StartWorkAsync()
{
this.WorkAsync().Forget();
}
但是 ASP.NET 会计算正在运行的任务的数量,因此它不适用于上面列出的简单Forget()
扩展名,而是可能会失败并出现例外情况:
在异步操作仍处于挂起状态时完成异步模块或处理程序。
使用.NET 4.5.2可以使用HostingEnvironment.QueueBackgroundWorkItem
:
public static Task HandleFault(this Task task, CancellationToken cancelToken)
{
return task.ContinueWith(
t => { WriteLog(t.Exception); },
cancelToken,
TaskContinuationOptions.OnlyOnFaulted,
TaskScheduler.Default);
}
public async Task StartWorkAsync()
{
System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(
cancelToken => this.WorkAsync().HandleFault(cancelToken));
}
答案 1 :(得分:99)
使用C#7,您现在可以使用discards:
_ = WorkAsync();
答案 2 :(得分:37)
您可以使用以下属性修饰方法:
[System.Diagnostics.CodeAnalysis.SuppressMessage("Await.Warning", "CS4014:Await.Warning")]
static async Task StartWorkAsync()
{
WorkAsync();
// ...
}
基本上你告诉编译器你知道自己在做什么,而且不需要担心可能的错误。
此代码的重要部分是第二个参数。 " CS4014:"部分是抑制警告的部分。你可以写任何你想要的东西。
答案 3 :(得分:30)
我处理这个问题的两种方式。
只是压制它
#pragma warning disable 4014
是一个很好的解决方案,可以解雇并忘记"。
存在此警告的原因是因为在许多情况下,您不打算使用在不等待的情况下返回任务的方法。当你打算开火和忘记时,抑制警告是有道理的。
如果您无法记住如何拼写#pragma warning disable 4014
,只需让Visual Studio为您添加即可。按Ctrl +。打开"快速行动"然后"抑制CS2014"
将其保存到本地变量
实施例
var task = Task.Run(() => DoMyStuff()).ConfigureAwait(false);
由于没有后续调用task
,因此在发布模式下,应立即优化局部变量,就像它永远不存在一样。事实上,我怀疑编译器甚至会创建局部变量(有人可以用反编译器确认它。)
总而言之
创建一个需要更多滴答来执行的方法是愚蠢的,只是为了抑制警告。
答案 4 :(得分:10)
停止警告的一种简单方法是在调用时简单地分配任务:
Task fireAndForget = WorkAsync(); // No warning now
在您的原始帖子中,您会这样做:
static async Task StartWorkAsync()
{
// Fire and forget
var fireAndForget = WorkAsync(); // Tell the compiler you know it's a task that's being returned
// more unrelated async/await stuff here, e.g.:
// ...
await Task.Delay(2000);
}
答案 5 :(得分:3)
警告的原因是WorkAsync返回一个永远不会读取或等待的 Task
。您可以将WorkAsync的返回类型设置为 void
,警告将消失。
通常,当调用者需要知道worker的状态时,方法返回Task
。在发生火灾和遗忘的情况下,应返回void以使调用者独立于被调用的方法。
static async void WorkAsync()
{
await Task.Delay(1000);
Console.WriteLine("Done!");
}
static async Task StartWorkAsync()
{
WorkAsync(); // no warning since return type is void
// more unrelated async/await stuff here, e.g.:
// ...
await Task.Delay(2000);
}
答案 6 :(得分:2)
为什么不将它包装在返回void的异步方法中?有点冗长,但使用了所有变量。
static async Task StartWorkAsync()
{
async void WorkAndForgetAsync() => await WorkAsync();
WorkAndForgetAsync(); // no warning
}
答案 7 :(得分:1)
我今天偶然发现了这种方法。您可以定义一个委托并将异步方法首先分配给该委托。
delegate Task IntermediateHandler();
static async Task AsyncOperation()
{
await Task.Yield();
}
并这样称呼
(new IntermediateHandler(AsyncOperation))();
...
我认为有趣的是,在使用委托时编译器不会给出完全相同的警告。