禁止“警告CS4014:因为没有等待此调用,所以继续执行当前方法......”

时间:2014-03-25 09:15:15

标签: c# async-await

这不是"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

8 个答案:

答案 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))();

...

我认为有趣的是,在使用委托时编译器不会给出完全相同的警告。