通过字符串数组进行多次webrequest

时间:2015-06-26 16:37:49

标签: c# arrays webrequest webresponse

我有超过2000个url调用make,并且下面的代码需要大约2分钟才能完成。有人可以帮助我加快这个过程吗?

private void button4_Click(object sender, EventArgs e)
    {
        WebRequest req;
        WebResponse res;
        string[] lines = File.ReadAllLines(@"c:\data\temp.txt");
        for (int i = 0; i < lines.Count(); i++)
        {
            req = WebRequest.Create(lines[i]); 
            res = req.GetResponse();
            StreamReader rd = new StreamReader(res.GetResponseStream(), Encoding.ASCII);
            rd.Close();
            res.Close();
            textBox1.Text += ".";
        }
    } 

非常感谢

3 个答案:

答案 0 :(得分:0)

你无法加快速度,因为瓶颈是你的互联网连接。但是你可以做些什么:

1)不要LINQ计数行,它是一个数组,它的大小是已知的(微优化,你不会注意到这种变化)。

2)使用using释放一次性对象(与速度无关,更好的错误处理:如果您的代码出现问题,您将使用GC释放资源)。

3)使它们平行。这将加速 little 位:

private void button4_Click(object sender, EventArgs e)
{
    var lines = File.ReadAllLines(@"c:\data\temp.txt");

    var options = new ParallelOptions { MaxDegreeOfParallelism = 4 };
    Parallel.ForEach(lines, options, line => 
    {
        var request = WebRequest.Create(line);

        using (var response = request.GetResponse()) 
        {
            var reader = new StreamReader(response.GetResponseStream(), Encoding.ASCII);

            // Do your stuff

            BeginInvoke(new MethodInvoker(delegate 
            {
                textBox1.Text += ".";
            }));
        }
    });
} 

几点注释:

  • MaxDegreeOfParallelism设置最大并发请求数。多个活动的并发连接不会无限期地加速事情,甚至可能减慢速度。某些试验可帮助您将此值设置为合理的值。

  • 没有任何错误检查,但网络事物可能暂时出错,但在短暂的延迟后,它们可能会按预期工作。我建议您还阅读System.Net.WebException: The remote name could not be resolvedthis进行I / O操作。

要使其成为更强大的完整示例,您的点击偶数处理程序将为:

private void button4_Click(object sender, EventArgs e)
{
    var options = new ParallelOptions { MaxDegreeOfParallelism = 4 };
    Parallel.ForEach(ReadUrlList(@"c:\data\temp.txt"), options, ProcessUrl);
}

处理每个网址和阅读网址列表的实际代码:

private static string[] ReadUrlList(string path)
{
    return File.ReadAllLines(@"c:\data\temp.txt");
}

private void ProcessUrl(string url)
{
    ProcessResponse(response =>
    {
        using (var reader = new StreamReader(response.GetResponseStream(), Encoding.ASCII))
       {
            // Do your stuff

            // We're working on separate threads, to access UI we
            // have to dispatch the call to UI thread. Note that
            // code will be executed asynchronously then local
            // objects may have been disposed!
            BeginInvoke(new MethodInvoker(delegate 
            {
                textBox1.Text += ".";
            }));
        }
    });
} 

使用此辅助方法隐藏网络操作的尝试/等待模式:

private static void ProcessResponse(string url, Action<WebResponse> action) 
{
    for (int i=1; i <= NumberOfRetries; ++i) 
    {
        try 
        {
            var request = WebRequest.Create(line);

            using (var response = request.GetResponse()) 
            {
                action(response);
            }

            break;
        }
        catch (Exception e) 
        {
            if (i == NumberOfRetries)
                throw;

            Thread.Sleep(DelayOnRetry);
        }
    }
}

private const int NumberOfRetries = 3;
private const int DelayOnRetry = 1000;

答案 1 :(得分:0)

我建议您使用Microsoft的Reactive Framework。 NuGet“Rx-Main”,“Rx-WinForms”/“Rx-WPF”。

这是代码的样子:

private void button4_Click(object sender, EventArgs e)
{
    var query =
        from line in File.ReadAllLines(@"c:\data\temp.txt").ToObservable()
        from result in Observable.Defer(() =>
        {
            var req = WebRequest.Create(line);
            return
                Observable.Using(
                    () => req.GetResponse(),
                    res => Observable.Using(
                        () => new StreamReader(res.GetResponseStream(), Encoding.ASCII),
                        st => Observable.Start(() => st.ReadToEnd())));
        })
        select new { line, result };

    query
        .ObserveOn(textBox1)
        .Subscribe(x => textBox1.Text += ".");
}

我假设您正在尝试从流中读取字符串。

这段代码很好地处理了所有中间对象。它还正确地多线程处理请求,并将结果编组到UI线程并更新文本框文本。

此代码的稍微清洁版本是:

private void button4_Click(object sender, EventArgs e)
{
    var query =
        from line in File.ReadAllLines(@"c:\data\temp.txt").ToObservable()
        from result in Observable.Using(
            () => new WebClient(),
            wc => Observable.Start(() => wc.DownloadString(new Uri(line))))
        select new { line, result };

    query
        .ObserveOn(textBox1)
        .Subscribe(x => textBox1.Text += ".");
}

它使用WebClient进行下载。它仍然是多线程的。

答案 2 :(得分:-1)

由于您未指定框架版本,因此我假设您使用的版本至少为4.5。

您可以使用ActionBlock轻松地同时执行多个调用。 ActionBlock在单个线程中执行其action方法,并且可以同时执行多个执行。

您可以使用以下内容:

var options=new ExecutionDataflowBlockOptions
{
    MaxDegreeOfParallelism = 10
}

var block=new ActionBlock<string>(url=>
{
    using(var req = WebRequest.Create(url))
    using(var res = req.GetResponse())
    {
       //Process the response here   
    }
});

string[] lines = File.ReadAllLines(@"c:\data\temp.txt");
foreach(var line in lines)
{
    block.Post(line);
}

block.Complete();

await block.Completion;

您可以通过更改MaxDegreeOfParallelism方法来控制并发请求的数量。

您也可以调用GetResponseAsync异步执行请求。这不会让它们变得更快,但它会减少用于提供相同数量请求的ThreadPool线程的数量。这意味着在阻塞和上下文切换时浪费的CPU更少。

var block=new ActionBlock<string>(url=>async 
{
    using(var req = WebRequest.Create(url))
    using(var res = await req.GetResponseAsync())
    {
       //Process the response here   
    }
});

处理请求和响应重要。除非您处置响应,否则与服务器的连接仍保持活动状态。 .NET强制每个域(即URL)限制2个并发请求,因此孤立的响应可能会导致延迟,直到垃圾收集器运行并收集它们。当可以覆盖限制时,最好始终处理回复。