120

我正在创建一个简单的 wpf 桌面应用程序。UI 在 .cs 文件中只有一个按钮和代码。

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

在 promise 风格的任务上可能不会调用 Start。

任何人都可以帮助为什么会这样?

4

1 回答 1

193

您收到该错误是因为该Task课程在将任务交给您之前已经开始了该任务。您只应该Start通过调用其构造函数来调用您创建的任务,甚至不应该这样做,除非您有令人信服的理由在创建任务时不启动该任务;如果您希望它立即启动,您应该使用Task.RunorTask.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直接使用会更难、更混乱。它还极大地简化了错误处理,去掉了很多样板代码。

于 2013-03-01T21:34:12.620 回答