为什么我会得到一个“等待激活”(WaitingforActivation)的任务状态?

6

我试图在使用VS2012框架4.5的表单上使用async和await命令。我的异步方法SlowMethodAsync不会返回任何内容。请注意,这段代码在控制台应用程序中可以正常工作。

private void button1_Click(object sender, EventArgs e)
{
    var task =  SlowMethodAsync();

    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine(i);
    }

    System.Threading.Tasks.TaskStatus status = task.Status;

     Console.WriteLine("Slow method result on Thread: {0}", task.Result);    //This line never executes
    Console.WriteLine("Main complete on {0}", Thread.CurrentThread.ManagedThreadId);
}

//Why is this method not returning anything?

static async Task<int> SlowMethodAsync()
{
    Console.WriteLine("Slow method started on Thread: {0}", Thread.CurrentThread.ManagedThreadId);

    await Task.Delay(2000);

    Console.WriteLine("Slow method complete on Thread: {0}", Thread.CurrentThread.ManagedThreadId);

    return 42;
}
1个回答

7

你引起了死锁。

使用task.Result会阻塞UI线程 - 直到SlowMethodAsync返回的任务完成,它才能完成。

然而,因为SlowMethodAsync最初是在UI线程上启动同步上下文的,所以在await之后的继续执行也希望在UI线程上执行。

因此,Result无法完成,直到异步方法完成...而异步方法无法完成,直到Result完成。

相反,你应该将你的button1_Click方法也设置为异步,并使用:

Console.WriteLine("Slow method result on Thread: {0}", await task); 

这段代码在控制台应用程序中可以正常工作是因为 SlowMethodAsync 方法没有任何特定的同步上下文需要返回,所以继续执行可以在任何线程池线程上执行 - 这不会被 "主" 线程等待任务的阻塞。

2
@marr75:WinForms UI线程方面有大量的文档资料。同样,关于异步方法的作用也有很多信息,尽管这显然是一个更高级的话题。在我看来,问题不在于缺乏文档,而在于缺乏人们阅读文档。 - Jon Skeet
1
@marr75:上下文捕获/恢复在MSDN、博客、async FAQ和async介绍文章中都有很好的文档。特别是死锁场景,最早在2011年1月(在MSDN博客上)被明确记录下来,在Channel9上演示了两次,在MSDN杂志上发表,并且仍然在SO和MSDN论坛上反复提问和回答。不确定还能在哪里或如何记录... - Stephen Cleary
现在代码可以工作了,但是我只返回了一个空值,而不是我想要的JSON项。 - Eman
@php-jquery-programmer:我建议您提出一个更具上下文的新问题 - 只有这些信息是无法帮助您的。 - Jon Skeet
@Jon Skeet:我实际上已经弄清楚了,尽管我遇到了另一个问题,即使我添加了断点并运行代码时明确看到数据,仍然返回NULL。 - Eman
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接