在承诺风格的任务中可能不会调用 Start。异常即将出现

我正在创建一个简单的 wpf 桌面应用程序。

private void Button_Click_2(object sender, RoutedEventArgs e)
{
FunctionA();
}


public void FunctionA()
{
Task.Delay(5000).Start();
MessageBox.Show("Waiting Complete");
}

但令人惊讶的是 Task.Delay(5000).Start();台词却是 InvalidOperationException:

在承诺风格的任务中可能不会调用 Start。

谁能告诉我为什么会这样?

99101 次浏览

之所以出现这个错误,是因为 Task类在将任务交给您之前已经启动了该任务。你应该只在你通过调用它的构造函数创建的任务上调用 Start,而且你甚至不应该这样做,除非你有一个令人信服的理由不在创建任务的时候启动它; 如果你想马上启动它,你应该使用 Task.Run或者 Task.Factory.StartNew来创建和启动一个新的 Task

所以,现在我们知道只要摆脱那讨厌的 Start。您将运行您的代码并发现消息框立即显示,而不是5秒后显示,这是怎么回事?

那么,Task.Delay只是给你一个任务,将在5秒钟内完成。它不会在5秒内停止线程的执行。您需要做的是在任务完成后执行一些代码。这就是 ContinueWith的作用。它允许您在完成给定的任务后运行一些代码:

public void FunctionA()
{
Task.Delay(5000)
.ContinueWith(t =>
{
MessageBox.Show("Waiting Complete");
});
}

这将按预期行事。

我们还可以利用 C # 5.0的 await关键字更容易地添加延续:

public async Task FunctionA()
{
await Task.Delay(5000);
MessageBox.Show("Waiting Complete");
}

虽然对这里发生的事情的完整解释超出了这个问题的范围,但最终结果是一个行为非常类似于前一个方法的方法; 它会在你调用该方法5秒后显示一个消息框,但是在这两种情况下,该方法本身都会立即返回。也就是说,await非常强大,并且允许我们编写看起来简单直接的方法,但是直接使用 ContinueWith编写方法会更加困难和混乱。它还极大地简化了错误处理,删除了大量样板代码。