2017-02-14 68 views
3

在ASP.NET 4.5应用程序中,哪些更适合从同步方法调用异步方法?Task.Run vs null SynchronizationContext

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核心外的过滤器和剃须刀的观点不是异步,所以如果我想从过滤器或剃刀视图调用异步方法,我需要以某种方式同步它。由于SynchronizationContext,只是使用SomethingAsync().GetAwaiter().GetResult()导致死锁,所以我需要一种方法来运行此代码而不需要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")); 
+0

'Task.Run'方法稍微更明显一些,'SynchronizationContext.Current'方法稍微有点高性能。哪一个“更好”纯粹是一个意见问题。 –

+0

我应该说“更好”。好的,所以在我看来,第二种情况没有什么问题,所以如果它包含在一个漂亮的静态函数中,听起来应该很好用 – Marius

+0

你不应该需要'if'检查。 'SetSynchronizationContext(null)'是有效的。 –

回答

1

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

Task.Run拥有SetSynchronizationContext(null)一些问题:

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

最后,另一个建议是use something like AsyncPump.Run (Stephen Toub)当你阻止Task返回函数。它等待排队延续由阻塞线程运行,这样您就不会支付多个并发线程的代价,并且不会发生死锁,显然还不如完全使用异步。