那种异步感觉 - httpclient和mvc线程阻塞

时间:2014-04-19 20:55:36

标签: c# .net asp.net-mvc caching async-await

困境,困境......

我一直在为使用HttpClient库(GetAsync=>ConfigureAwait(false)等)的异步调用的问题制定解决方案。在一个控制台应用程序中,我的dll非常敏感,使用异步等待调用和Parallel.ForEach(=>)的混合使我发光。

现在问题。从这个测试工具移动到目标应用程序后,事情变得有问题。我使用asp.net mvc 4并遇到了一些问题。主要问题是在控制器操作上调用我的进程实际上会阻塞主线程,直到异步操作完成。我尝试使用async控制器模式,我尝试过使用Task.Factory,我尝试使用new Threads。你说它,我尝试了所有的味道 - 然后是一些!。

现在,我很欣赏http的本质并不是为了促进像这样的漫长过程,而且这里有很多篇文章说不要这样做。但是,为什么我需要使用这种方法有减轻的原因。我需要在mvc中运行它的主要原因是我实际上是通过在我的dll代码中引发事件来实时更新实时数据缓存(在mvc应用程序上)。这意味着在整个异步操作完成之前,可以实时推送50-60个数据源的片段。因此,客户端应用程序可以在发出异步操作的几秒钟内收到部分更新。如果我将流程委托给在后台运行整个流程的控制台应用程序,我将无法再利用这些片段部分更新,这是整个选择背后的存在理由。这个架构。

任何人都可以阐明一个允许我减轻线程阻塞的解决方案,同时允许每个异步片段被我的对象模型使用并输出到客户端应用程序(I&#39 ; m使用signalr进行这些客户端更新。一种 nirvanna 将是一个场景,其中进程外缓存对象可以在众多进程之间共享 - 然后缓存更新可以由我的mvc进程触发和使用(aka - {{3 }})。所以回到现实......

我还考虑过使用辅助Web服务来实现这一目标,但是再次欢迎其他选项over engineering我的解决方案(已经有很多移动部件和大量异步Actions正在进行中)

很抱歉没有添加任何代码,我希望获得实用的哲学/见解,而不是代码帮助,当然欢迎编码示例来解释我的问题。

随着时间的推移,我会及时更新问题,因为我的思维过程仍然在成熟。

[edit] - 为了清楚起见,下面的代码段是我兄弟的grimm代码碰撞(从更大的工作中提取):

Parallel.ForEach(scrapeDataBases, new ParallelOptions()
{
    MaxDegreeOfParallelism = Environment.ProcessorCount * 15
}, 
async dataBase =>
{
    await dataBase.ScrapeUrlAsync().ConfigureAwait(false);
    await UpdateData(dataType, (DataCheckerScrape)dataBase);
});

2 个答案:

答案 0 :(得分:5)

asyncParallel.ForEach不会自然混合,因此我不确定您的控制台解决方案是什么样的。此外,Parallel几乎不应该在ASP.NET上使用。

听起来你只想使用Task.WhenAll

另外,我认为你在ASP.NET上进行后台处理的原因是不正确的。完全有可能有一个单独的过程通过SignalR更新客户端。

答案 1 :(得分:1)

因为你的问题很高,没有很多代码。您可以尝试Reactive Extensions。

这样的东西
private IEnumerable<Task<Scraper>> ScrappedUrls()
{
    // Return the 50 to 60 task for each website here. 
    // I assume they all return the same type.
    // return .ScrapeUrlAsync().ConfigureAwait(false);
    throw new NotImplementedException();
}

public  async Task<IEnumerable<ScrapeOdds>> GetOdds()
{
    var results = new Collection<ScrapeOdds>();
    var urlRequest = ScrappedUrls();
    var observerableUrls = urlRequest.Select(u => u.ToObservable()).Merge();
    var publisher = observerableUrls.Publish();
    var hubContext = GlobalHost.ConnectionManager.GetHubContext<OddsHub>();
    publisher.Subscribe(scraper =>
        {
            // Whatever you do do convert to the result set
            var scrapedOdds = scraper.GetOdds();
            results.Add(scrapedOdds);

            // update anything else you want when it arrives.

            // Update SingalR here 
            hubContext.Clients.All.UpdatedOdds(scrapedOdds);
        });
    // Will fire off subscriptions and not continue until they are done.
    await publisher;
    return results;
}

合并选项将在结果进入时处理结果。然后您可以更新signalR集线器以及进入时需要更新的任何其他内容。控制器操作必须等待它们全部进入。这就是为什么出版商正在等待。

我真的不知道httpClient是否愿意同时进行50-60次网络通话。如果不是,你可以将IEnumerable带到一个数组并将其分解为一个较小的块。而且那里应该有一些错误检查。使用Rx,您也可以将它告诉SubscribeOn和ObserverOn不同的线程,但我认为所有内容都非常不同,这是不必要的。