【问题标题】: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# 编写...

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


【解决方案1】:

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

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

在您的情况下,当您在未完成的任务上使用 Task.Result 属性时,您会同步阻塞 UI 线程,因此当 CheckInternetConnectionAsyncReturnARunningTask 完成后尝试在 SynchronizationContext 上恢复时,它可以吨。 UI 线程在等待CheckInternetConnectionAsync 任务时被阻塞,该任务又等待 UI 线程,因此出现死锁(无限期阻塞)。

使用Task.Run(() =&gt; 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)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-03-26
  • 2013-06-30
  • 1970-01-01
  • 2020-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多