取消警告CS4014:因为此调用未等待,当前方法的执行仍在继续…

本文关键字:方法 执行 继续 CS4014 警告 因为 等待 调用 取消 | 更新日期: 2023-09-27 17:52:52

这不是"如何在没有await的情况下安全地调用c#中的async方法"的副本。

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

警告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
    // ...
}

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

取消警告CS4014:因为此调用未等待,当前方法的执行仍在继续…

在c# 7中可以使用discards:

_ = WorkAsync();

您可以创建一个扩展方法来阻止该警告。扩展方法可以为空,也可以在其中添加.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));
}

我有两种处理方法。

保存为丢弃变量(c# 7)

例子

_ = Task.Run(() => DoMyStuff()).ConfigureAwait(false);

自从c# 7中引入了discards,我现在认为这比抑制警告要好。因为它不仅抑制了警告,而且使"即发即弃"的意图更加明确。

此外,编译器将能够在发布模式下优化它。

直接抑制

#pragma warning disable 4014
...
#pragma warning restore 4014

是一个很好的解决方案,"炒了就忘了"。

存在此警告的原因是因为在许多情况下,您不打算使用不等待返回task的方法。当你确实想开火却忘记时,抑制警告是有意义的。

如果你不记得如何拼写#pragma warning disable 4014,只需让Visual Studio为你添加它。按Ctrl +。打开"快速操作",然后打开"抑制CS2014"

All in All

仅仅为了抑制警告而创建一个需要更多时间来执行的方法是愚蠢的。

你可以用以下属性来修饰这个方法:

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

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

这段代码的重要部分是第二个参数。"CS4014:"部分是抑制警告的部分。

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

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); 
}

警告的原因是WorkAsync正在返回一个从未被读取或等待的 Task 。您可以将WorkAsync的返回类型设置为 void ,警告将消失。

当调用者需要知道工作线程的状态时,方法通常返回一个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); 
}

为什么不把它包装在一个返回void的async方法中呢?有点长,但是使用了所有的变量。

static async Task StartWorkAsync()
{   
     async void WorkAndForgetAsync() => await WorkAsync();
     WorkAndForgetAsync(); // no warning
}

我今天偶然发现了这个方法。你可以先定义一个委托,然后把async方法分配给这个委托。

    delegate Task IntermediateHandler();

    static async Task AsyncOperation()
    {
        await Task.Yield();
    }

并这样命名

(new IntermediateHandler(AsyncOperation))();

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