使用HttpWebRequest.BeginGetResponse下载未定义数量的文件

时间:2011-03-19 06:48:02

标签: multithreading locking httpwebrequest

我必须编写一个下载几千个文件的小应用程序。其中一些文件包含对必须作为同一过程的一部分下载的其他文件的引用。以下代码下载了初始文件列表,但我想将其他文件作为同一循环的一部分下载。这里发生的是循环在第一个请求返回之前完成。知道如何实现这个目标吗?

var countdownLatch = new CountdownEvent(Urls.Count);

string url;
while (Urls.TryDequeue(out url))
{
    HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
    webRequest.BeginGetResponse(
        new AsyncCallback(ar =>
        {
            using (HttpWebResponse response = (ar.AsyncState as HttpWebRequest).EndGetResponse(ar) as HttpWebResponse)
            {
                using (var sr = new StreamReader(response.GetResponseStream()))
                {
                    string myFile = sr.ReadToEnd();

                    // TODO: Look for a reference to another file. If found, queue a new Url.
                }
            }
        }), webRequest);
}

ce.Wait();

2 个答案:

答案 0 :(得分:1)

我们想到的一个解决方案是跟踪待处理请求的数量,并且只有在没有请求待处理且Url队列为空时才完成循环:

string url;
int requestCounter = 0;
int temp;
AutoResetEvent requestFinished = new AutoResetEvent(false);
while (Interlocked.Exchange(requestCounter, temp) > 0 || Urls.TryDequeue(out url))
{
    if (url != null)
    {
        Interlocked.Increment(requestCounter);
        HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
        webRequest.BeginGetResponse(
            new AsyncCallback(ar =>
            {
                try {
                    using (HttpWebResponse response = (ar.AsyncState as HttpWebRequest).EndGetResponse(ar) as HttpWebResponse)
                    {
                        using (var sr = new StreamReader(response.GetResponseStream()))
                        {
                            string myFile = sr.ReadToEnd();

                            // TODO: Look for a reference to another file. If found, queue a new Url.
                        }
                    }
                } 
                finally { 
                    Interlocked.Decrement(requestCounter); 
                    requestFinished.Set();
                }
            }), webRequest);
    }
    else
    {
        // no url but requests are still pending
        requestFinished.WaitOne();
    }
}

答案 1 :(得分:1)

您正在尝试编写一个webcrawler。为了编写一个好的webcrawler,首先需要定义一些参数...

1)您想同时下载多少个请求?换句话说,您想要多少吞吐量?这将决定你想要多少个请求,线程池大小应该是什么等等。

2)您必须拥有一个URL队列。每个完成的请求都会填充此队列。您现在需要确定队列的增长策略是什么。例如,您不能拥有无限制的队列,因为您可以比从网络下载更快地将工作项抽入队列。

鉴于此,您可以按如下方式设计系统:

拥有实际从网上下载的最多N个工作线程。他们从队列中抽出一次,然后下载数据。他们解析数据并填充您的URL队列。

如果超过' M'队列中的URL,然后是队列块,不允许对队列进行排队。现在,在这里你可以做两件事之一。您可以使排队的线程阻塞,也可以放弃排队的工作项。一旦另一个工作项在另一个线程上完成,并且一个URL出列,阻塞的线程现在就能够成功排队。

使用这样的系统,您可以确保在下载数据时不会耗尽系统资源。

实现:

请注意,如果您使用的是异步,则使用额外的I / O线程进行下载。只要你注意到这个事实,这很好。你可以做一个纯粹的异步实现,你可以在这里实现N' N' BeginGetResponse()非常出色,对于每个完成的,你都会启动另一个。这样你总会有'N' N' N'请求未完成。