繁体   English   中英

Task.Run vs null SynchronizationContext

[英]Task.Run vs null SynchronizationContext

在ASP.NET 4.5应用程序中,哪一个更适合从同步方法调用异步方法?

var result = Task.Run(() => SomethingAsync()).GetAwaiter().GetResult();

// or 

var temp = SynchronizationContext.Current;
try
{
    SynchronizationContext.SetSynchronizationContext(null);
    return SomethingAsync().GetAwaiter().GetResult();
}
finally
{
    SynchronizationContext.SetSynchronizationContext(temp);
}

注意:是的,我知道我应该使用async/await一直向下,但我问的是最底层,在ASP.NET Core之外,过滤器和剃刀视图不是异步的,所以如果我想调用从过滤器或剃刀视图的异步方法,我需要以某种方式同步它。 只是使用SomethingAsync().GetAwaiter().GetResult()导致死锁,因为SynchronizationContext ,所以我需要一种方法来运行没有SynchronizationContext代码。

编辑这是一个简单的帮助类,它干净地包装它:

public static class Async
{
    public static T Run<T>(Func<Task<T>> func)
    {
        var context = SynchronizationContext.Current;
        if (context == null)
        {
            return func().GetAwaiter().GetResult();
        }

        SynchronizationContext.SetSynchronizationContext(null);

        try
        {
            return func().GetAwaiter().GetResult();
        }
        finally
        {
            SynchronizationContext.SetSynchronizationContext(context);
        }
    }

    public static void Run(Func<Task> func)
    {
        var context = SynchronizationContext.Current;
        if (context == null)
        {
            func().GetAwaiter().GetResult();
            return;
        }

        SynchronizationContext.SetSynchronizationContext(null);

        try
        {
            func().GetAwaiter().GetResult();
        }
        finally
        {
            SynchronizationContext.SetSynchronizationContext(context);
        }
    }
}

// Example
var result = Async.Run(() => GetSomethingAsync("blabla"));

显然,同步调用异步代码并不理想,但如果你必须我会说如果你可以帮助它,请避免使用Task.Run

Task.RunSetSynchronizationContext(null)有许多问题:

  • 目前尚不清楚为什么要使用它,你想在Web服务器上启动新任务? 如果没有评论,这将被新开发者快速删除。 然后繁荣,难以诊断生产问题(死锁)。
  • 它在一个新的线程池线程上启动,直到它到达它的第一个等待完成的延续,而不是同步运行。 (这是一个非常小的优化)
  • 如果您从多个级别执行此操作以保护您的SynchronizationContext它会使您同步阻止整个任务返回功能,而不仅仅是需要它的区域,每次使用它时,您也会倍增您的问题。 您最终会对异步代码进行更长时间的阻止,这肯定不是您想要的。
  • 如果事实证明没有阻塞/死锁你认为存在,或者它随后被修复, Task.Run现在正在引入阻塞异步,而SetSynchronizationContext(null)将不会花费你任何东西。

最后,另一个建议是在阻止Task返回功能时使用AsyncPump.Run (Stephen Toub)之类的东西 它以阻塞线程运行排队延续的方式等待,这样你就不会支付多个并发线程的价格而且没有死锁,显然仍然没有像一直使用异步一样好。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM