同步等待一个异步操作,为什么Wait()会在这里冻结程序 [英] Synchronously waiting for an async operation, and why does Wait() freeze the program here

查看:15
本文介绍了同步等待一个异步操作,为什么Wait()会在这里冻结程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

前言:我在寻找一个解释,而不仅仅是一个解决方案.我已经知道解决方案了.

Preface: I'm looking for an explanation, not just a solution. I already know the solution.

尽管花了几天时间研究 MSDN 上关于基于任务的异步模式 (TAP)、async 和 await 的文章,我仍然对一些更精细的细节感到困惑.

Despite having spent several days studying MSDN articles about the Task-based Asynchronous Pattern (TAP), async and await, I'm still a bit confused about some of the finer details.

我正在为 Windows 应用商店应用编写一个记录器,我想同时支持异步和同步日志记录.异步方法遵循TAP,同步方法应该隐藏所有这些,并且看起来和工作起来像普通方法.

I'm writing a logger for Windows Store Apps, and I want to support both asynchronous and synchronous logging. The asynchronous methods follow the TAP, the synchronous ones should hide all this, and look and work like ordinary methods.

这是异步日志的核心方法:

This is the core method of asynchronous logging:

private async Task WriteToLogAsync(string text)
{
    StorageFolder folder = ApplicationData.Current.LocalFolder;
    StorageFile file = await folder.CreateFileAsync("log.log",
        CreationCollisionOption.OpenIfExists);
    await FileIO.AppendTextAsync(file, text,
        Windows.Storage.Streams.UnicodeEncoding.Utf8);
}

现在对应的同步方法...

Now the corresponding synchronous method...

版本 1:

private void WriteToLog(string text)
{
    Task task = WriteToLogAsync(text);
    task.Wait();
}

这看起来是正确的,但它不起作用.整个程序永远冻结.

This looks correct, but it does not work. The whole program freezes forever.

版本 2:

嗯.. 也许任务没有开始?

Hmm.. Maybe the task was not started?

private void WriteToLog(string text)
{
    Task task = WriteToLogAsync(text);
    task.Start();
    task.Wait();
}

这会抛出 InvalidOperationException: Start 可能不会在 Promise 风格的任务上被调用.

版本 3:

嗯.. Task.RunSynchronously 听起来很有希望.

Hmm.. Task.RunSynchronously sounds promising.

private void WriteToLog(string text)
{
    Task task = WriteToLogAsync(text);
    task.RunSynchronously();
}

这会抛出 InvalidOperationException: RunSynchronously 不能在未绑定到委托的任务上调用,例如从异步方法返回的任务.

第 4 版(解决方案):

private void WriteToLog(string text)
{
    var task = Task.Run(async () => { await WriteToLogAsync(text); });
    task.Wait();
}

这有效.因此,2 和 3 是错误的工具.但是 1?1 有什么问题,和 4 有什么区别?是什么让 1 导致冻结?任务对象有问题吗?是否存在不明显的僵局?

This works. So, 2 and 3 are the wrong tools. But 1? What's wrong with 1 and what's the difference to 4? What makes 1 cause a freeze? Is there some problem with the task object? Is there a non-obvious deadlock?

推荐答案

异步方法中的 await 正在尝试返回 UI 线程.

The await inside your asynchronous method is trying to come back to the UI thread.

由于 UI 线程忙于等待整个任务完成,因此您会陷入死锁.

Since the UI thread is busy waiting for the entire task to complete, you have a deadlock.

将异步调用移动到 Task.Run() 解决了这个问题.
由于异步调用现在在线程池线程上运行,因此它不会尝试返回 UI 线程,因此一切正常.

Moving the async call to Task.Run() solves the issue.
Because the async call is now running on a thread pool thread, it doesn't try to come back to the UI thread, and everything therefore works.

或者,您可以在等待内部操作之前调用 StartAsTask().ConfigureAwait(false) 使其返回线程池而不是 UI 线程,从而完全避免死锁.

Alternatively, you could call StartAsTask().ConfigureAwait(false) before awaiting the inner operation to make it come back to the thread pool rather than the UI thread, avoiding the deadlock entirely.

这篇关于同步等待一个异步操作,为什么Wait()会在这里冻结程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆