Task.Run vs null SynchronizationContext

时间:2017-02-14 10:05:53

标签: c# asp.net asp.net-mvc asynchronous razor

在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核心的底部和外部过滤器和剃刀视图不是异步的,所以如果我想从过滤器或剃刀视图调用异步方法,我需要以某种方式同步它。由于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"));

1 个答案:

答案 0 :(得分:1)

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

Task.RunSetSynchronizationContext(null)上面有很多问题:

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

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

相关问题