在Async函数上调用Result会无限期地导致阻塞吗?

时间:2015-02-03 01:11:14

标签: c# .net async-await task c#-5.0

我从一个沉重的java背景中对C#有点新手,并且仍在试图弄清楚await和async是如何工作的,这里有什么令我困惑的事情:

假设我有一个这样的函数(下面的Hackish代码仅用于实验):

public static bool CheckInternetConnection()
{
    Task<bool> result = CheckInternetConnectionAsync();
    return result.Result;
}

public async static Task<bool> CheckInternetConnectionAsync()
{
    if (NetworkInterface.GetIsNetworkAvailable())
    {
        SomeClass details = await ReturnARunningTask();
        return details.IsSuccessful;
    }
    return false;
}

public Task<SomeClass> ReturnARunningTask()
{
    return Task.Run(() => checkInternet());
}

在主执行线程中,如果我这样称呼:

CheckInternetConnection();

它将无限期地阻塞,我的假设是控制在&#34; await&#34;处留下CheckInternetConnectionAsync()。关键字和块&#34; .Result&#34;。等待恢复时,主线程已被阻止并保持阻塞状态

我假设的原因是我可以看到任务完成并返回,但是await之后的代码永远不会被执行

然而,如果我这样做:

bool result = Task.Run(() => CheckInternetConnection()).Result;

然后执行await语句后的代码并且主线程继续

我的期望是它也会阻塞,因为主线程会在中间任务上被阻塞。中间任务将在.Result

上被阻止

所以...有什么不同?在这种情况下,await执行后的代码是什么?

1 个答案:

答案 0 :(得分:14)

在UI环境中,您有一个特殊的单线程SynchronizationContext,可以在UI线程上运行所有内容。当您await任务时以及任务完成时该方法将在捕获的上下文上恢复时捕获该上下文(可以使用ConfigureAwait进行配置:

SomeClass details = await ReturnARunningTask().ConfigureAwait(false);

如果您在未完成的任务上使用Task.Result属性,则同步阻止UI线程,因此CheckInternetConnectionAsync尝试在SynchronizationContext之后继续ReturnARunningTask {1}}完成它不能。 UI线程被阻塞等待CheckInternetConnectionAsync任务,后者又等待UI线程,因此死锁(无限期阻塞)。

使用Task.Run(() => CheckInternetConnection()).Result;时的区别在于使用Task.Run卸载工作要在ThreadPool线程上完成。这些线程没有SynchronizationContext,因此在.Result上阻止UI线程时,任务可以完成,因为它不需要UI线程来执行此操作。

不鼓励阻止异步操作(sync over async),因为它会阻止线程并使应用程序响应性和可伸缩性降低,并且可能导致死锁。 您应该一直使用async-await


注意:您应该使同步版本保持同步,而不是执行“async同步”,如果需要将{1}}版本的CPU密集型工作卸载到其他线程,请使用{{ 1}}:

async