在约定样式的任务上可能不会调用启动。异常来了


108

我正在创建一个简单的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

在约定样式的任务上可能不会调用启动。

有人可以帮忙吗?

Answers:


171

之所以收到该错误,是因为Task该类在将任务提供给您之前已经启动了任务。您应该只Start通过调用其构造函数来调用所创建的任务,除非您有充分的理由在创建任务时不启动该任务,否则甚至不应这样做。如果您希望它立即启动,则应该使用Task.RunTask.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直接使用它们编写起来会更加困难和混乱。它也大大简化了错误处理,节省了大量样板代码。


1

试试这个。

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

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

-4

正如Servy所说的,该任务已经开始,所以剩下要做的就是等待它(.Wait()):

private void Button_Click_2(object sender, RoutedEventArgs e)
{
    FunctionA();
}
public void FunctionA()
{
    Task.Delay(5000).Wait();
    MessageBox.Show("Waiting Complete");
}

1
调用Wait()任务将阻塞当前线程,直到任务解决。那几乎永远不会是您想要发生的事情。
杰里米

1
@Jeremy:确实,您应该注意您提到的行为,但是在这种情况下,他的FunctionA已经阻塞了当前线程,因此我认为他只是在寻找一种确定任务何时完成的方法。为了弄清楚Wait和async(面向以后的读者)之间的区别,请阅读此链接
Sergiu 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.