在异步函数上调用结果是不是会导致无限期阻塞?
Posted
技术标签:
【中文标题】在异步函数上调用结果是不是会导致无限期阻塞?【英文标题】:Does Calling Result on an Async function cause block indefinitely?在异步函数上调用结果是否会导致无限期阻塞? 【发布时间】:2015-04-02 02:33:56 【问题描述】:我是 C# 的新手,来自沉重的 java 背景,但仍在试图弄清楚 await 和 async 究竟是如何工作的,这让我很困惑:
假设我有一个这样的函数(下面的黑客代码仅用于实验):
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();
它将无限期地阻塞,我的假设是控制将 CheckInternetConnectionAsync() 留在“await”关键字并阻塞“.Result”。当 await 恢复时,主线程已经被阻塞并保持阻塞状态
我假设的原因是我可以看到任务完成并返回,但是await之后的代码永远不会执行
但是,如果我这样做:
bool result = Task.Run(() => CheckInternetConnection()).Result;
然后执行await语句后的代码,主线程继续执行
我的预期是它也会阻塞,因为主线程会在中间任务上被阻塞。中间任务会在 .Result 上被阻塞
所以..有什么不同?在这种情况下,await之后的代码会被执行吗?
【问题讨论】:
这是什么类型的应用程序?控制台,wpf? 它是一个在 android 上运行的 xamarin 应用程序,用 C# 编写... 【参考方案1】:在 UI 环境中,您有一个特殊的单线程 SynchronizationContext
,它运行 UI 线程上的所有内容。当您 await
一个任务时,该上下文被捕获,当任务完成时,该方法在该捕获的上下文中恢复(这可以使用 ConfigureAwait
进行配置:
SomeClass details = await ReturnARunningTask().ConfigureAwait(false);
在您的情况下,当您在未完成的任务上使用 Task.Result
属性时,您会同步阻塞 UI 线程,因此当 CheckInternetConnectionAsync
在 ReturnARunningTask
完成后尝试在 SynchronizationContext
上恢复时,它可以吨。 UI 线程在等待CheckInternetConnectionAsync
任务时被阻塞,该任务又等待 UI 线程,因此出现死锁(无限期阻塞)。
使用Task.Run(() => CheckInternetConnection()).Result;
时的区别在于,使用Task.Run
卸载要在ThreadPool
线程上完成的工作。这些线程没有 SynchronizationContext
,因此当 UI 线程在 .Result
上被阻塞时,任务可以完成,因为它不需要 UI 线程来执行此操作。
不鼓励阻塞异步操作 (sync over async
),因为它会阻塞线程并使应用程序的响应性和可扩展性降低,并且可能导致死锁。 您应该一直使用async-await
。
注意:不要执行“通过 async
同步”,您应该让同步版本同步,如果您需要将 CPU 密集型工作卸载到不同线程的 async
版本,请使用 Task.Run
:
public static bool CheckInternetConnection()
return checkInternet().IsSuccessful;
public async static Task<bool> CheckInternetConnectionAsync()
if (NetworkInterface.GetIsNetworkAvailable())
return await Task.Run(() => CheckInternetConnection());
return false;
【讨论】:
问题,所以如果我从 UI 线程调用 await,而后者又调用 await 等等并且不使用任何 TPL 东西,UI SyncContext 是否总是在 await 时传递? @MatthewYang 是的。除非您使用Task,否则它将一次又一次地传递。运行或 ConfigureAwait(false)以上是关于在异步函数上调用结果是不是会导致无限期阻塞?的主要内容,如果未能解决你的问题,请参考以下文章