Вы пытаетесь написать веб-сканер. Чтобы написать хороший веб-сканер, сначала нужно определить некоторые параметры ...
1) Сколько запросов вы хотите загрузить одновременно? Другими словами, какую пропускную способность вы хотите? Это будет определять такие вещи, как количество запросов, ожидающих обработки, размер пула потоков и т. Д.
2) У вас должна быть очередь URL-адресов. Эта очередь заполняется каждым запросом, который завершается. Теперь вам нужно решить, какова стратегия роста очереди. Например, у вас не может быть неограниченной очереди, поскольку вы можете закачивать рабочие элементы в очередь быстрее, чем вы можете загрузить из сети.
Учитывая это, вы можете спроектировать систему следующим образом:
Максимум N рабочих потоков, которые фактически загружаются из Интернета. Они берут один раз из очереди и загружают данные. Они анализируют данные и заполняют вашу очередь URL.
Если в очереди более чем «M» URL-адресов, то очередь блокируется и больше не позволяет размещать URL-адреса в очереди. Теперь здесь вы можете сделать одну из двух вещей. Вы можете либо заблокировать поток, который ставится в очередь, либо просто отказаться от ставимого в очередь рабочего элемента. Как только другой рабочий элемент завершается в другом потоке и URL-адрес удаляется, заблокированный поток теперь сможет успешно ставить в очередь.
В такой системе вы можете быть уверены, что при загрузке данных у вас не закончатся системные ресурсы.
Реализация:
Обратите внимание, что если вы используете async, то для загрузки вы используете дополнительный поток ввода-вывода. Это хорошо, если вы помните об этом факте. Вы можете сделать чистую асинхронную реализацию, где у вас может быть N N BeginGetResponse (), и для каждой завершающей работы вы запускаете другую. Таким образом, вы всегда будете иметь N запросов.