为什么await不会等待?

4
这是我的实际代码:
    async Task getData()
    {
        Thread.Sleep(5000);
        Console.WriteLine("Step 1");

        using (HttpClient api = new HttpClient()) 
            await api.GetAsync("http://google.com/").ContinueWith(
                (getTask) =>
                    Console.WriteLine(getTask.Result.StatusCode);
            );

        Console.WriteLine("Step 2");
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Task<Task> task = new Task<Task>(getData);
        task.Start();
        task.Wait();
        Console.WriteLine("Step 3");
    }

我得到了以下输出:
Step 1
Step 3
OK
Step 2

为什么第三步不在第二步之后?

如何使它按顺序工作,即在getData中的所有操作完成后才返回给调用者?


2
尝试使用 task.Result.Wait(),但您的示例似乎有点过于复杂。 - Christian.K
谢谢!!!它起作用了。能解释一下为什么吗?控制流是如何绕过getData方法的结尾并返回给调用者的? - Old Geezer
3
看起来你应该真正阅读 /学习一下有关异步/等待的适当介绍(例如这篇文章)。简而言之:getData已经返回一个任务,通过将其包装在另一个任务中,你必须等待内部任务。或者,在 button1_Click 中直接编写 await getData(...)。但要注意,异步/等待还有更多内容 - 真的需要更多地学习它。 - Christian.K
此外,您不应该使用Task构造函数(https://blog.stephencleary.com/2014/05/a-tour-of-task-part-1-constructors.html)。 - Stephen Cleary
2个回答

3

您还应将事件处理程序标记为async

private async Task button1_Click(object sender, EventArgs e)

使用await代替Wait

private async Task button1_Click(object sender, EventArgs e)
{
    await getData();

    Console.WriteLine("Step 3");
}

当我们使用async/await模式时,应该遵循此方法,直到第一个方法启动它。否则,通过显式调用WaitResult,我们可能会遇到死锁问题。此外,显式调用这些方法会阻塞执行线程。因此,您放弃了使用async/await的主要优势,即不阻塞执行线程,并在单独的线程上运行一些代码,一旦完成,从停止的位置恢复代码的执行。

谢谢。我不知道事件处理程序可以是“async”。这是否意味着调用事件处理程序的所有代码也都是“async”? - Old Geezer
@不用谢。这意味着事件可以异步处理,而不会阻塞您的应用程序。例如,如果我们谈论一个WPF应用程序,当单击按钮触发网络调用时,如果相应的处理程序不是异步的,则您的UI将冻结,因为它需要一些相当长的时间才能完成。 - Christos

2
简而言之,async Task(和<T>)方法,本身负责分配代表它们完成的Task对象。如果你发现自己正在使用async方法并且手动分配Task对象(直接分配或使用静态工厂方法如Task.Run),那么要认识到此时存在多个Task对象。你需要小心并识别出创建返回TaskTask的情况。

如果可能的话,最好从头到尾完全使用async(将每个函数修改为async后,然后倾向于定位每个调用站点并使该位置也成为async),并停止手动分配任何Task


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