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

Mat*_*ang 4 .net c# task async-await 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());
}
Run Code Online (Sandbox Code Playgroud)

在主执行线程中,如果我调用它:

CheckInternetConnection();
Run Code Online (Sandbox Code Playgroud)

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

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

但是,如果我这样做:

bool result = Task.Run(() => CheckInternetConnection()).Result;
Run Code Online (Sandbox Code Playgroud)

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

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

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

i3a*_*non 14

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

SomeClass details = await ReturnARunningTask().ConfigureAwait(false);
Run Code Online (Sandbox Code Playgroud)

在您Task.Result未完成的任务中使用该属性的情况下,您同步阻止UI线程,因此当CheckInternetConnectionAsync尝试SynchronizationContextReturnARunningTask完成后继续尝试时它不能.UI线程被阻塞等待CheckInternetConnectionAsync任务,而该任务又等待UI线程,因此死锁(无限期阻塞).

使用时的区别Task.Run(() => CheckInternetConnection()).Result;在于使用Task.Run卸载工作要在ThreadPool线程上完成.这些线程没有SynchronizationContext,所以当UI线程被阻塞时.Result,任务就可以完成,因为它不需要UI线程这样做.

不鼓励阻止异步操作(同步结束async),因为它会阻止线程并使应用程序响应性和可伸缩性降低,并且可能导致死锁.您应该使用async-await所有的方式.


注意:async您应该使同步版本保持同步,而不是进行"同步",如果您需要async将CPU密集型工作卸载到其他线程的版本,请使用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;
}
Run Code Online (Sandbox Code Playgroud)