【问题标题】:ASP.NET Multithreading Web RequestsASP.NET 多线程 Web 请求
【发布时间】:2014-05-28 13:05:54
【问题描述】:

我正在我的 ASP.NET 解决方案中构建一个页面,该页面从第 3 方 API 服务中检索其大部分数据。

页面本身需要对 API 进行大约 5 次不同的单独调用以填充其所有控件,因为对 API 的每个 Web 请求都会返回不同的数据集。

我想同时处理我在新线程上发出的每个单独的 Web 请求,以减少加载时间。

我提出的每个请求都像这样:-

WebRequest request = WebRequest.Create(requestUrl);

string response = new StreamReader(request.GetResponse().GetResponseStream()).ReadToEnd();

return new JsonSerializer().Deserialize<OccupationSearch>(new JsonTextReader(new StringReader(response)));

首先,我应该尝试这个吗?即,它安全吗?它会通过多线程来提高性能吗?

其次,最好的方法是什么?多线程系统有很多不同的方法,但哪种方法最适合这项任务?

【问题讨论】:

  • 每个 Web 请求应该已经是 IIS 中的不同工作线程,具体取决于您的服务承受的负载量以及 IIS 的设置方式。因此,如果您谈论多线程,您将尝试将每个请求完成的工作细分为多个线程。
  • 我会看看异步执行它。使用request.GetResponseAsync() 等人。

标签: c# asp.net multithreading webrequest


【解决方案1】:

多线程是正确的选择,但我会称之为异步处理

无论如何,您应该知道多线程在 IIS 中的工作方式不同。

一旦所有子线程结束,IIS 工作进程就会完成一个请求,这是一个大问题,因为您不想长时间持有一个工作进程,而是将其重新用于其他请求。其实就是一个线程池。

这就是 ASP.NET 提供自己的方法来实现异步的原因,如果使用正确的方法,IIS 将能够一次处理更多请求,因为异步工作将在 IIS 进程模型之外执行。

我建议您阅读有关 ASP.NET 异步的更多信息:

结论:使用异步工作,这将更有效地利用服务器资源,但首先要了解如何以正确的方式进行操作!

【讨论】:

  • 感谢您的建议。
  • @Derek 没问题。事实上,你应该这样做,因为其他方法会以死锁告终。
【解决方案2】:

多线程不推荐在 ASP.NET 上使用;因为 ASP.NET/IIS 有自己的多线程,你做的任何多线程都会干扰这些启发式。

您真正想要的是 并发,更具体地说是 异步并发,因为您的操作是 I/O 绑定的。

最好的方法是使用HttpClientTask-based Asynchronous Pattern

public async Task<OccupationSearch> GetOccupationAsync(string requestUrl)
{
  // You can also reuse HttpClient instances instead of creating a new one each time
  using (var client = new HttpClient())
  {
    var response = client.GetStringAsync(requestUrl);
    return new JsonSerializer().Deserialize<OccupationSearch>(new JsonTextReader(new StringReader(response)));
  }
}

这是异步的,您可以使用 Task.WhenAll 轻松使其并发:

List<string> urls = ...;
OccupationSearch[] results = await Task.WhenAll(urls.Select(GetOccupationAsync));

【讨论】:

  • 感谢您的建议
【解决方案3】:

我认为多线程(在合理范围内)会带来好处,因为您的代码编写方式知道对 GetResponse().GetResponseStream() 的调用是阻塞的。

提高性能的最简单方法之一是使用Parallel.ForEach

        var urls = new List<string>();

        var results = new ConcurrentBag<OccupationSearch>();

        Parallel.ForEach(urls, url =>
        {
            WebRequest request = WebRequest.Create(requestUrl);

            string response = new StreamReader(request.GetResponse().GetResponseStream()).ReadToEnd();

            var result = JsonSerializer().Deserialize<OccupationSearch>(new JsonTextReader(new StringReader(response)));

            results.Add(result);
        });

如果您使用的是 .NET 4.5,则新方法是使用 async/await。 Msdn 在这里有一篇关于这个主题的相当广泛的文章:http://msdn.microsoft.com/en-us/library/hh300224.aspxhttp://msdn.microsoft.com/en-us/library/hh696703.aspx

Scott Hanselman 也有一篇关于这个主题的好看的博客文章:http://www.hanselman.com/blog/TheMagicOfUsingAsynchronousMethodsInASPNET45PlusAnImportantGotcha.aspx

【讨论】:

  • 您不需要lock(results),因为List&lt;T&gt; 不是线程安全的(用于写入)?
  • 公平点,老实说,我不确定 List 是否会丢失任何东西,因为它不是读写的。但请注意安全,我会更新以使用 ConcurrentBag。
  • 列表正在写入 - 它正在写入结果的Add-ition。你只需要一个lock 就可以了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-20
  • 1970-01-01
  • 2013-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多