编译器警告(等级 1)CS4014

由于此调用不会等待,因此在调用完成前将继续执行当前方法。请考虑将“await”运算符应用于调用结果。

当前方法调用返回 TaskTask<TResult>,而不应用运算符 等候 到结果的异步方法。异步方法的调用启动异步任务。但是,因为 await 运算符没有应用,程序继续运行,而不必等待任务完成。在大多数情况下,此行为不是您所期望的。通常调用方法的其他特性取决于调用的结果,或最低限度上,调用方法的预计在从包含调用的方法返回之前完成。

同样至关重要的问题是被调用的异步方法中引发的异常发生了什么。在返回TaskTask<TResult>方法中引发的异常被存储在返回的任务中。如果不等待任务还不显式检查异常,该异常会丢失。如果在等待任务,异常会被重新抛出。

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

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

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


async Task CallingMethodAsync()
{
    resultsTextBox.Text += "\r\n  Entering calling method.";
    // Variable delay is used to slow down the called method so that you can
    // distinguish between awaiting and not awaiting in the program's output.
    // You can adjust the value to produce the output that this topic shows
    // after the code.
    var delay = 5000;

    // 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(delay);

    // 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(delay);

    // 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(delay);

    // If the call to CalledMethodAsync isn't awaited, CallingMethodAsync
    // continues to run and, in this example, finishes its work and returns
    // to its caller.
    resultsTextBox.Text += "\r\n  Returning from calling method.";
}

async Task CalledMethodAsync(int howLong)
{
    resultsTextBox.Text += 
        "\r\n    Entering called method, starting and awaiting Task.Delay.";

    // Slow the process down a little so that you can distinguish between
    // awaiting and not awaiting in the program's output. Adjust the value
    // for howLong if necessary.
    await Task.Delay(howLong);
    resultsTextBox.Text += 
        "\r\n    Task.Delay is finished--returning from called method.";
}

在此示例中,如果您调用Call #1或Call #2,在调用方 (CallingMethodAsync) 和调用方的调用方 (startButton_Click) 完成后,不等待异步方法 (CalledMethodAsync) 。当调用的方法完成时,以下输出的最后一行向你演示了。从在完整的示例中调用 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(C# 参考) 指令,还可以取消编译器警告。

以下 Windows Presentation Foundation (WPF) 应用程序包含从前面的方法。下列步骤建立应用程序。

  1. 创建 WPF 应用程序,将其命名为 AsyncWarning

  2. 在 Visual Studio 代码编辑器中,选择“MainWindow.xaml”选项卡。

    如果此选项卡不可视,则在“解决方案资源管理器”中,打开 MainWindow.xaml 的快捷菜单,然后选择“查看代码”。

  3. 在 MainWindow.xaml 的“XAML”视图中,使用下面的代码替换代码。

    &lt;Window x:Class="AsyncWarning.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525"&gt;
        &lt;Grid&gt;
            &lt;Button x:Name="startButton" Content="Start" HorizontalAlignment="Left" Margin="214,28,0,0" VerticalAlignment="Top" Width="75" HorizontalContentAlignment="Center" FontWeight="Bold" FontFamily="Aharoni" Click="startButton_Click" /&gt;
            &lt;TextBox x:Name="resultsTextBox" Margin="0,80,0,0" TextWrapping="Wrap" FontFamily="Lucida Console"/&gt;
        &lt;/Grid&gt;
    &lt;/Window&gt;
    

    包含按钮和文本框的简单窗口显示在 MainWindow.xaml 的“设计”窗口中。

    有关 XAML设计器的更多信息,请参见在 Visual Studio 中,使用 XAML 设计器创建 UI。有关如何生成自己的简单的 UI,请参见 演练:使用 Async 和 Await 访问 Web(C# 和 Visual Basic)的“创建 WPF 应用程序”和“设计的简单 WPF MainWindow”一节。

  4. 将MainWindow.xaml.cs中的代码替换为以下代码。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace AsyncWarning
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private async void startButton_Click(object sender, RoutedEventArgs e)
            {
                resultsTextBox.Text += "\r\nEntering the Click event handler.";
                await CallingMethodAsync();
                resultsTextBox.Text += "\r\nExiting the Click event handler.";
            }
    
            async Task CallingMethodAsync()
            {
                resultsTextBox.Text += "\r\n  Entering calling method.";
                // Variable delay is used to slow down the called method so that you can
                // distinguish between awaiting and not awaiting in the program's output.
                // You can adjust the value to produce the output that this topic shows
                // after the code.
                var delay = 5000;
    
                // 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(delay);
    
                // 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(delay);
    
                // 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(delay);
    
                // If the call to CalledMethodAsync isn't awaited, CallingMethodAsync
                // continues to run and, in this example, finishes its work and returns
                // to its caller.
                resultsTextBox.Text += "\r\n  Returning from calling method.";
            }
    
            async Task CalledMethodAsync(int howLong)
            {
                resultsTextBox.Text += 
                    "\r\n    Entering called method, starting and awaiting Task.Delay.";
    
                // Slow the process down a little so that you can distinguish between
                // awaiting and not awaiting in the program's output. Adjust the value
                // for howLong if necessary.
                await Task.Delay(howLong);
                resultsTextBox.Text += 
                    "\r\n    Task.Delay is finished--returning from called method.";
            }
        }
    
        // Output with Call #1 or Call #2\. (Wait for the last line to appear.)
    
        // 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.
    
        // Output with Call #3, which awaits the call to CalledMethodAsync.
    
        // Entering the Click event handler.
        //   Entering calling method.
        //     Entering called method, starting and awaiting Task.Delay.
        //     Task.Delay is finished--returning from called method.
        //   Returning from calling method.
        // Exiting the Click event handler.
    }
    
  5. 选择 F5 键运行程序,然后选择“开始”按钮。

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

请参阅

await(C# 参考)

使用 Async 和 Await 的异步编程(C# 和 Visual Basic)