Загрузите неопределенное количество файлов с помощью HttpWebRequest.BeginGetResponse - PullRequest
0 голосов
/ 19 марта 2011

Я должен написать небольшое приложение, которое загружает несколько тысяч файлов.Некоторые из этих файлов содержат ссылки на другие файлы, которые должны быть загружены как часть того же процесса.Следующий код загружает исходный список файлов, но я хотел бы загрузить другие файлы как часть того же цикла.Здесь происходит то, что цикл завершается до возвращения первого запроса.Есть идеи как этого добиться?

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 ]

1 голос
/ 21 марта 2011

Вы пытаетесь написать веб-сканер. Чтобы написать хороший веб-сканер, сначала нужно определить некоторые параметры ...

1) Сколько запросов вы хотите загрузить одновременно? Другими словами, какую пропускную способность вы хотите? Это будет определять такие вещи, как количество запросов, ожидающих обработки, размер пула потоков и т. Д.

2) У вас должна быть очередь URL-адресов. Эта очередь заполняется каждым запросом, который завершается. Теперь вам нужно решить, какова стратегия роста очереди. Например, у вас не может быть неограниченной очереди, поскольку вы можете закачивать рабочие элементы в очередь быстрее, чем вы можете загрузить из сети.

Учитывая это, вы можете спроектировать систему следующим образом:

Максимум N рабочих потоков, которые фактически загружаются из Интернета. Они берут один раз из очереди и загружают данные. Они анализируют данные и заполняют вашу очередь URL.

Если в очереди более чем «M» URL-адресов, то очередь блокируется и больше не позволяет размещать URL-адреса в очереди. Теперь здесь вы можете сделать одну из двух вещей. Вы можете либо заблокировать поток, который ставится в очередь, либо просто отказаться от ставимого в очередь рабочего элемента. Как только другой рабочий элемент завершается в другом потоке и URL-адрес удаляется, заблокированный поток теперь сможет успешно ставить в очередь.

В такой системе вы можете быть уверены, что при загрузке данных у вас не закончатся системные ресурсы.

Реализация:

Обратите внимание, что если вы используете async, то для загрузки вы используете дополнительный поток ввода-вывода. Это хорошо, если вы помните об этом факте. Вы можете сделать чистую асинхронную реализацию, где у вас может быть N N BeginGetResponse (), и для каждой завершающей работы вы запускаете другую. Таким образом, вы всегда будете иметь N запросов.

1 голос
/ 19 марта 2011

Одно решение, которое приходит на ум, - это отслеживать количество ожидающих запросов и завершать цикл только после того, как ни один запрос не будет ожидающим, а очередь 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();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...