编译器警告(等级 1)CS4014

在调用完成之前,会继续执行当前方法,原因是此调用不处于等待状态。 请考虑向调用结果应用 await 运算符。

当前方法调用返回 TaskTask<TResult> 的异步方法,而不对结果应用 await 运算符。 对异步方法的调用会启动异步任务。 但是,由于没有应用 await 运算符,程序将继续运行,而不必等待任务完成。 在大多数情况下,此行为并不是您所期望的。 方法调用的其他方面通常取决于调用的结果,而调用的方法至少应在从包含调用的方法返回之前完成。

对于被调用的异步方法中引发的异常,如何进行处理同样是一个重要的问题。 在返回 TaskTask<TResult> 的方法中引发的异常将存储在返回的任务中。 如果不等待任务或不显式检查异常,该异常会丢失。 如果等待任务,将重新引发异常。

作为最佳做法,应始终等待调用。

只有当您确定不需要等待异步调用完成并且调用的方法不会引发任何异常时,才应考虑禁止显示警告。 在此情况下,可以通过将调用的任务结果分配给变量来禁止显示警告。

下面的示例演示如何生成警告,如何禁止显示警告以及如何等待调用。

static async Task CallingMethodAsync(int millisecondsDelay)
{
    Console.WriteLine("  Entering calling method.");

    // Call #1.
    // Call an async method. Because you don't await it, its completion
    // isn't coordinated with the current method, CallingMethodAsync.
    // The following line causes warning CS4014.
    CalledMethodAsync(millisecondsDelay);

    // Call #2.
    // 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, recommended practice is always to
    // await a call to an async method.

    // Replace Call #1 with the following line.
    // Task delayTask = CalledMethodAsync(millisecondsDelay);

    // Call #3
    // To contrast with an awaited call, replace the unawaited call
    // (Call #1 or Call #2) with the following awaited call. Best
    // practice is to await the call.

    // await CalledMethodAsync(millisecondsDelay);

    Console.WriteLine("  Returning from calling method.");
}

static async Task CalledMethodAsync(int millisecondsDelay)
{
    Console.WriteLine("    Entering called method, starting and awaiting Task.Delay.");

    await Task.Delay(millisecondsDelay);

    Console.WriteLine("    Task.Delay is finished--returning from called method.");
}

在此示例中,如果选择调用 #1 或调用 #2,未等待的异步方法 CalledMethodAsync 将在调用方 CallingMethodAsync 和调用方的调用方完成后完成。 以下输出的最后一行演示了调用的方法何时完成。 输出内容标示了整个示例中何时进入和退出调用 CallingMethodAsync 的事件处理程序。

Entering the Click event handler.
  Entering calling method.
    Entering called method, starting and awaiting Task.Delay.
  Returning from calling method.
Exiting the Click event handler.
    Task.Delay is finished--returning from called method.

通过使用 #pragma warning 指令,也可以禁止显示编译器警告。

示例

以下控制台应用程序包含上一示例中的方法。 下列步骤用于设置该应用程序。

  1. 创建控制台应用程序并将其命名为 AsyncWarning

  2. 在 Visual Studio Code 编辑器中,选择“Program.cs”文件。

  3. 将 Program.cs 中的代码替换为以下代码。

    using System;
    using System.Threading.Tasks;
    
    namespace AsyncWarning
    {
        class Program
        {
            static async Task Main()
            {
                Console.WriteLine("Entering Main() application entry point.");
    
                int millisecondsDelay = 2000;
                await CallingMethodAsync(millisecondsDelay);
    
                Console.WriteLine("Exiting Main() application entry point.");
    
                await Task.Delay(millisecondsDelay + 500);
            }
    
            static async Task CallingMethodAsync(int millisecondsDelay)
            {
                Console.WriteLine("  Entering calling method.");
    
                // Call #1.
                // Call an async method. Because you don't await it, its completion
                // isn't coordinated with the current method, CallingMethodAsync.
                // The following line causes warning CS4014.
                // CalledMethodAsync(millisecondsDelay);
    
                // Call #2.
                // 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, recommended practice is always to
                // await a call to an async method.
    
                // Replace Call #1 with the following line.
                //Task delayTask = CalledMethodAsync(millisecondsDelay);
    
                // Call #3
                // To contrast with an awaited call, replace the unawaited call
                // (Call #1 or Call #2) with the following awaited call. Best
                // practice is to await the call.
    
                // await CalledMethodAsync(millisecondsDelay);
    
                Console.WriteLine("  Returning from calling method.");
            }
    
            static async Task CalledMethodAsync(int millisecondsDelay)
            {
                Console.WriteLine("    Entering called method, starting and awaiting Task.Delay.");
    
                await Task.Delay(millisecondsDelay);
    
                Console.WriteLine("    Task.Delay is finished--returning from called method.");
            }
        }
    
        // Output with Call #1 or Call #2. (Wait for the last line to appear.)
    
        // Entering Main() application entry point.
        //   Entering calling method.
        //     Entering called method, starting and awaiting Task.Delay.
        //   Returning from calling method.
        // Exiting Main() application entry point.
        //     Task.Delay is finished--returning from called method.
    
        // Output with Call #3, which awaits the call to CalledMethodAsync.
    
        // Entering Main() application entry point.
        //   Entering calling method.
        //     Entering called method, starting and awaiting Task.Delay.
        //     Task.Delay is finished--returning from called method.
        //   Returning from calling method.
        // Exiting Main() application entry point.
    }
    
  4. 选择 F5 键以运行该项目。

预期的输出显示在代码末尾。

请参阅