2015-05-25 24 views
1

我读过很多帖子,人们面临类似的问题,但他们似乎做出不适用的假设或他们的代码根本不适用于我。我需要结合异步方法的结果。 我想要的唯一的异步是结果的组合。由于Azure服务总线一次只允许我抓取256条消息,因此我想发送多个请求以一次获得几个批次,并将其制作成一个列表。结合异步方法和同步返回

似乎是,如果你调用一个异步 方法要返回给调用者的假设而完成的各项工作 (即:需要长时间运行的任务)。但是,我根本不需要这个。我 想等待任务完成,然后把我的组合列表 并返回它。

首先,我不想用async标记我的调用方法。我可以,但为什么我应该,我无意中调用了一个同步方法,在返回给我之前恰好执行一些异步操作。

我看过使用WhenAll()然后使用Result的例子,但这对我不起作用。我尝试了所有不同的排列,但它会锁定我的应用程序,或者它告诉我任务尚未执行。

这是我目前有:

public IEnumerable<BrokeredMessage>[] GetCombinedResults() 
{ 
      var job =() => ServiceBus.TrackerClient.ReceiveBatchAsync(BatchLimit); 
      Task<IEnumerable<BrokeredMessage>> task1 = _retryPolicy.ExecuteAsync<IEnumerable<BrokeredMessage>>(job); 
      Task<IEnumerable<BrokeredMessage>> task2 = _retryPolicy.ExecuteAsync<IEnumerable<BrokeredMessage>>(job); 
      IEnumerable<BrokeredMessage>[] results = Task.WhenAll<IEnumerable<BrokeredMessage>>(task1, task2).Result; 
      return results; 
} 

但调用的结果导致其锁定。我已经读过这样的僵局,但是在其他问题上已经看到了这个问题。 如果我调用Task.WaitAll()并且不在意结果,这种类型的设置工作正常。不知道为什么当我想要从任务返回结果时变得困难。我尝试过使用Task.Run,​​但是在获得结果之前它会退出我的方法。

回答

5

您似乎对使用异步进行扇出并行感兴趣。这是一个完全有效的事情。没有必要使整个调用链异步,以利用扇出并行。

你偶然发现了通常的ASP.NET死锁。您可以使用Task.Run作为一种简单的,故障安全的方式来避免它。首先,让我们GetCombinedResults异步保持它的简单和一致的:

public async Task<IEnumerable<BrokeredMessage>[]> GetCombinedResultsAsync() 
{ 
      var job =() => ServiceBus.TrackerClient.ReceiveBatchAsync(BatchLimit); 
      Task<IEnumerable<BrokeredMessage>> task1 = _retryPolicy.ExecuteAsync<IEnumerable<BrokeredMessage>>(job); 
      Task<IEnumerable<BrokeredMessage>> task2 = _retryPolicy.ExecuteAsync<IEnumerable<BrokeredMessage>>(job); 
      IEnumerable<BrokeredMessage>[] results = await Task.WhenAll<IEnumerable<BrokeredMessage>>(task1, task2); 
      return results; 
} 

这种方法显然是正确的。它不混合同步和异步。这样称呼:

var results = Task.Run(() => GetCombinedResultsAsync()).Result; 

这个原理的工作原理是GetCombinedResultsAsync现在没有同步上下文执行。

+0

谢谢!我仍然得到异步/等待的东西。我仍然不确定为什么WaitAll()非常简单。我不需要await关键字,我不必将当前方法标记为async。我只是创建一些任务并说WaitAll()。如果我想在突然完成任务后得到结果,我必须将当前方法标记为异步。似乎很奇怪。 – KingOfHypocrites

+0

我不知道为什么WaitAll可以工作。它不应该。 Task.Run的东西肯定应该工作。使用调试器找出挂起开始的代码段。或者,注释Task.Run行,看它是否可行。还有一些你还没有发布的相关代码。张贴更多的代码。 – usr

+0

@KingOfHypocrites:WaitAll()阻止当前线程等待所有任务完成,因此根据同步上下文(UI和Asp.NET)可能会发生死锁。 usr解决方案在执行异步方法时删除同步上下文。请参阅[this](http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html)和[this](https://msdn.microsoft.com/en-us/杂志/ gg598924.aspx)可以很好地解释同步上下文。 – Absolom