并发调用外部服务

时间:2018-06-08 21:14:51

标签: c# asp.net asp.net-mvc concurrency task

我目前正在开发一个项目,用于在现有的ASP.Net MVC网站和我公司正在使用的文件托管服务之间建立集成。典型的用例是:

  1. 用户请求一个或多个文件
  2. 控制器对文件主机API进行一次调用
  3. 文件主机将文件数据返回给控制器
  4. 控制器返回文件结果
  5. 托管服务可以处理并发呼叫,并且我发现在任务中执行每个API调用(参见下面的示例)会导致相当大的改进。

    private void RetrieveDocuments(DocumentIdentifier[] identifiers, List<FileHostResult> results)
    {
        var tasks = identifiers.Select(x => RetrieveDocument(results, x)).ToArray();
        Task.WaitAll(tasks);
    }
    
    private Task RetrieveDocument(List<FileHostResult> results, DocumentIdentifier x)
    {
        return Task.Run(() =>
        {
            var result = GetFileHostResultFromFileHost(x.ExternalIdentifier);
            lock (results)
            {
                results.Add(result);
            }
        });
    }
    

    我的问题是,是否有更好的方法可以做到这一点,或者是否有任何潜在的陷阱我可能遇到? (例如,锁定服务器资源等)。

    编辑1:我没有发布GetFileHostResultFromFileHost的代码,因为我没有任何改变它的权限。它基本上是在库中实现的方法调用我不能改变。

    编辑2:澄清。我主要担心的是避免损害网站上当前的用户体验。为此,我想确保从ASP.net mvc同时运行任务并不会锁定该站点。

2 个答案:

答案 0 :(得分:1)

您应该使用Microsoft的Reactive Framework来实现此目的。它非常适合这种处理。

以下是代码:

IObservable<FileHostResult> query =
    from i in identifiers.ToObservable()
    from r in Observable.Start(() => GetFileHostResultFromFileHost(i.ExternalIdentifier))
    select r;

IList<FileHostResult> results = query.ToList().Wait();

那就是它。它可以在最佳线程数上正确调度代码。

如果您想要等待代码,那么您可以这样做:

IObservable<FileHostResult> query =
    from i in identifiers.ToObservable()
    from r in Observable.Start(() => GetFileHostResultFromFileHost(i.ExternalIdentifier))
    select r;

IList<FileHostResult> results = await query.ToList();

它非常简单,易于编码。

NuGet&#34; System.Reactive&#34;然后将using System.Reactive.Linq;添加到您的代码中。

答案 1 :(得分:0)

如果没有看到其余的源代码,很难给出很好的建议。但根据我所看到的情况,我建议采用以下方法:

(app\db)

这种方法的优点:

  • 没有明确使用private void RetrieveDocuments(DocumentIdentifier[] identifiers, List<FileHostResult> results) { results.AddRange(identifiers.AsParallel().Select(x => RetrieveDocument(x))); } private FileHostResult RetrieveDocument(DocumentIdentifier x) { var result = GetFileHostResultFromFileHost(x.ExternalIdentifier); return result; } - 让Task.Run为您处理。
  • 无需锁定AsParallel列表 - 让resultsAsParallel为您处理

您可能还希望增加有权访问的maximum number个连接。

虽然诚实,但我认为你应该看看根本不需要新Select的方法 - 可能是使用Async http下载调用你可以运行{ {3}}没有线程的开销。