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

nos*_*tio 145 c# async-await

这不是"如何在没有等待的情况下安全地调用C#中的异步方法"的副本.

我如何很好地抑制以下警告?

警告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); 
}
Run Code Online (Sandbox Code Playgroud)

我尝试过但不喜欢的事情:

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
    // ...
}
Run Code Online (Sandbox Code Playgroud)

更新后,由于原始接受的答案已被编辑,我已将已接受的答案更改为使用C#7.0丢弃的答案,因为我认为ContinueWith这不合适.每当我需要记录"即发即弃"操作的异常时,我就会使用Stephen Cleary提出的更精细的方法.

Ant*_*ser 136

使用C#7,您现在可以使用丢弃:

_ = WorkAsync();
Run Code Online (Sandbox Code Playgroud)

  • 这是一个方便的小语言功能,我不记得了.就像我脑中有一个`_ = ......`. (7认同)
  • 我发现SupressMessage从我的Visual Studio“错误列表”中删除了我的警告,但没有“输出”,并且`#pragma warning disable CSxxxx`看起来比丢弃的更为丑陋;) (2认同)

Kna*_*ģis 120

您可以创建一个阻止警告的扩展方法.扩展方法可以为空,也可以在.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();
}
Run Code Online (Sandbox Code Playgroud)

但是,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));
}
Run Code Online (Sandbox Code Playgroud)

  • 我找到了[`TplExtensions.Forget`](http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.threading.tplextensions.forget.aspx).[Microsoft.VisualStudio.Threading`](http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.threading.aspx)下有很多好处.我希望它可以在Visual Studio SDK之外使用. (8认同)
  • @stricq为Forget()服务添加ConfigureAwait(false)的目的是什么?据我了解,ConfigureAwait仅影响在任务上使用await的点上的线程同步,但Forget()的目的是抛弃Task,因此永远无法等待Task,因此这里的ConfigureAwait毫无意义. (3认同)
  • 如果产生线程在火灾之前消失并且忘记任务完成,没有ConfigureAwait(false)它仍然会尝试将自己编组回产生线程,该线程消失,因此它会死锁.设置ConfigureAwait(false)告诉系统不要编组回调用线程. (3认同)
  • 此回复包含管理特定案例的编辑以及十几条评论.简单的事情往往是正确的事情,去丢弃!我引用@ fjch1997回答:**创建一个需要更多滴答来执行的方法是愚蠢的,只是为了抑制警告.** (2认同)

Fáb*_*bio 37

您可以使用以下属性修饰方法:

[System.Diagnostics.CodeAnalysis.SuppressMessage("Await.Warning", "CS4014:Await.Warning")]
static async Task StartWorkAsync()
{
    WorkAsync();
    // ...
}
Run Code Online (Sandbox Code Playgroud)

基本上你告诉编译器你知道你在做什么,而且不需要担心可能的错误.

这段代码的重要部分是第二个参数."CS4014:"部分是抑制警告的部分.你可以写下你想要的任何东西.

  • 这很有效,非常感谢! (2认同)

fjc*_*997 32

我的两种处理方式.

只是压制它

_ = Task.Run(() => DoMyStuff()).ConfigureAwait(false); 是一个很好的解决方案,"发射并忘记".

存在此警告的原因是因为在许多情况下,您不打算使用在不等待的情况下返回任务的方法.当你打算开火和忘记时,抑制警告是有道理的.

如果您无法记住如何拼写#pragma warning disable 4014,只需让Visual Studio为您添加即可.按Ctrl +.打开"快速操作"然后"抑制CS2014"

将其保存到本地变量

#pragma warning disable 4014

由于没有后续调用_ = Task.Run(() => DoMyStuff()).ConfigureAwait(false);,在Release模式下,本地变量应该立即优化,就像它从来没有那样.事实上,我怀疑编译器甚至会创建局部变量(有人可以用反编译器确认它).

总而言之

创建一个需要更多滴答来执行的方法是愚蠢的,只是为了抑制警告.

  • 不要忘记使用“#pragma warning disable 4014”之类的错误代码,然后使用“#pragma warning restore 4014”恢复警告。它仍然可以在没有错误代码的情况下工作,但是如果您不添加错误编号,它将禁止所有消息。 (2认同)

noe*_*cus 10

停止警告的一种简单方法是在调用时简单地分配任务:

Task fireAndForget = WorkAsync(); // No warning now
Run Code Online (Sandbox Code Playgroud)

所以在您的原始帖子中你会这样做:

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); 
}
Run Code Online (Sandbox Code Playgroud)


小智 8

警告的原因是 WorkAsync 返回的是Task从未读取或等待的值。您可以将 WorkAsync 的返回类型设置为void,警告就会消失。

Task通常,当调用者需要知道工作人员的状态时,方法会返回 a 。在“即发即弃”的情况下,应该返回 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); 
}
Run Code Online (Sandbox Code Playgroud)