【问题标题】:Concurrency Limit on HttpWebRequestHttpWebRequest 的并发限制
【发布时间】:2011-05-21 21:48:35
【问题描述】:
我正在编写一个应用程序来测量使用 C# 下载网页的速度。我提供一个唯一域名列表,然后生成 X 个线程并执行 HTTPWebRequests,直到使用完域列表。问题是无论我使用多少线程,我每秒只能获得大约 3 页。
我发现 System.Net.ServicePointManager.DefaultConnectionLimit 是 2,但我的印象是这与每个域的连接数有关。由于列表中的每个域都是唯一的,因此这应该不是问题。
然后我发现 GetResponse() 方法会阻止所有其他进程的访问,直到 WebResponse 关闭:http://www.codeproject.com/KB/IP/Crawler.aspx#WebRequest,我还没有在网上找到任何其他信息来支持这一说法,但是我实现了一个 HTTP 请求使用套接字,我注意到速度显着提高(4 倍到 6 倍)。
所以我的问题是:有人知道 HttpWebRequest 对象是如何工作的吗?除了上面提到的之外还有其他解决方法吗?或者是否有任何地方用 C# 编写的高速网络爬虫示例?
【问题讨论】:
标签:
c#
.net
multithreading
httpwebrequest
httpwebresponse
【解决方案1】:
您应该使用BeginGetResponse 方法,它不会阻塞并且是异步的。
在处理 I/O 绑定的异步时,仅仅因为您生成了一个线程来执行 I/O 工作,该线程仍然会被阻塞,等待硬件(在这种情况下是网卡)响应。如果您使用内置的 BeginGetResponse,那么该线程只会在网卡上排队,然后可以进行更多工作。硬件完成后,它会通知您,此时您的回调将被调用。
【解决方案2】:
您是否尝试过使用 BeginGetResponse() 等异步方法?
如果您使用的是 .net 4.0,您可能想尝试此代码。本质上,我使用任务在特定站点上发出 1000 个请求(我使用它在我的开发机器上对应用程序进行负载测试,我认为没有限制,因为我的应用程序会快速连续地看到这些请求)
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 1000; i++)
{
var webRequest = WebRequest.Create(textBox1.Text);
webRequest.GetReponseAsync().ContinueWith(t =>
{
if (t.Exception == null)
{
using (var sr = new StreamReader(t.Result.GetResponseStream()))
{
string str = sr.ReadToEnd();
}
}
else
System.Diagnostics.Debug.WriteLine(t.Exception.InnerException.Message);
});
}
}
}
public static class WebRequestExtensions
{
public static Task<WebResponse> GetReponseAsync(this WebRequest request)
{
return Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
}
}
由于这里的工作负载受 I/O 限制,因此不需要生成线程来完成工作,实际上可能会损害性能。在 WebClient 类上使用 Async 方法会使用 I/O 完成端口,因此性能会更高,资源消耗更少。
【解决方案3】:
我想指出BeginGetResponse 方法不是完全异步的:(来自MSDN)
BeginGetResponse 方法需要在此方法变为异步之前完成一些同步设置任务(例如 DNS 解析、代理检测和 TCP 套接字连接)。因此,永远不应在用户界面 (UI) 线程上调用此方法,因为它可能需要一些时间,通常是几秒钟。