编译器警告(等级 1)CS4014
由于此调用不会等待,因此在调用完成前将继续执行当前方法。请考虑将“await”运算符应用于调用结果。
当前方法调用返回 Task 或 Task<TResult>,不应用 等待 运算符应用于该结果的异步方法。 对异步方法的调用将启动异步任务。 但是,在中,因为 await 运算符不是应用的,程序将继续运行,而不等待任务完成。 在大多数情况下,此行为不是预期。 通常被调用的方法的其他方面依赖于调用的结果,或者最低限度上,调用方法预计完成,在从包含调用的方法之前返回。
同样重要的问题是发生在调用异步方法引发的异常。 在方法引发返回 Task 或 Task<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.";
}
在此示例中,因此,如果选择调用 #1 或调用 #2,在其调用方 (CallingMethodAsync) 之后的 unawaited 异步方法 (CalledMethodAsync) 完成,并且调用方的调用方 (startButton_Click) 已完成。 当调用方法完成时,以下输出的最后一行显示您。 项以及从输出中对该完整示例中的 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 演示基础 (WPF) 应用程序包含前面示例中的方法。 以下步骤将的应用程序。
创建一个 WPF 应用程序,并将其命名为 AsyncWarning。
在 Visual Studio 代码编辑器"中,选择 MainWindow.xaml 选项。
如果看不到选项卡,打开 MainWindow.xaml 的快捷菜单在 解决方案资源管理器,然后选择 查看代码。
用下面的代码替换在 MainWindow.xaml XAML 视图中的代码。
<Window x:Class="AsyncWarning.MainWindow" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <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" /> <TextBox x:Name="resultsTextBox" Margin="0,80,0,0" TextWrapping="Wrap" FontFamily="Lucida Console"/> </Grid> </Window>
包含一个按钮和文本框的简单窗口显示 MainWindow.xaml 设计 视图。
有关 XAML 设计器的更多信息,请参见 使用 XAML 设计器创建 UI。 有关如何生成简单的 UI 的信息,请参见 演练:使用 Async 和 Await 访问 Web(C# 和 Visual Basic)“创建 WPF 应用程序”和“设计简单的 WPF 的”部分。
用下面的代码替换在 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. }
选择 F5 键运行程序,然后选择 启动 按钮。
预期的输出显示在代码的结尾。