Как быстрее скачивать?

Каков самый быстрый способ загрузить исходный код веб-страницы в компонент заметки? Я использую компоненты Indy и HttpCli.

Проблема в том, что у меня есть список, заполненный более чем 100 сайтами, моя программа загружает исходный код в памятку и анализирует этот источник для файлов mp3. Это что-то вроде программы поиска музыки в Google; он использует запросы Google, чтобы упростить поиск в Google.

Я начал читать о потоках, которые приводят к моему вопросу: могу ли я создать экземпляр IdHttp в потоке с функцией синтаксического анализа и сказать ему, чтобы он анализировал половину сайтов в списке?

Итак, в основном, когда пользователь нажимает кнопку синтаксического анализа, основной поток должен делать:

for i := 0 to listbox1.items.count div 2 do
    get and parse

, а другой поток должен сделать:

for i := form1.listbox1.items.count div 2 to form1.listbox1.items.count - 1 do
    get and parse.

, чтобы они одновременно добавляли проанализированный контент в form1.listbox2. Или, может быть, проще запустить два экземпляра IdHttp в основном потоке; один для первой половины сайтов, а другой для второй?

Для этого: что использовать: Indy или Synapse?


person Danijel Maksimovic Maxa    schedule 06.11.2011    source источник
comment
Я бы посоветовал вам прочитать документацию о том, что делает Synchronize, и заставить каждый поток запрашивать один (и только один) URL-адрес при запуске и каждый раз после обработки одного URL-адреса. Если веб-сайты используют XHTML, я бы также проверил метод MSXML2_TLB DOMDocument.load, чтобы убедиться, что загрузка и синтаксический анализ работают хорошо.   -  person Stijn Sanders    schedule 06.11.2011


Ответы (3)


Я бы создал поток, который может читать один URL-адрес и обрабатывать его содержимое. Затем вы можете решить, сколько из этих потоков вы хотите запускать одновременно. Ваш компьютер будет допускать большое количество подключений, поэтому, если эти 100 сайтов имеют разные имена хостов, не проблема запустить 10 или 20 одновременно. Слишком много - перебор, а слишком мало - пустая трата времени процессора.

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

Потому что в настоящее время у вас есть другие проблемы. Во-первых, это не делается для использования компонентов VCL в потоке. Если вам нужна информация из списка в потоке, вам нужно будет либо использовать Synchronize в потоке, чтобы сделать «безопасный» вызов основного потока, либо вам придется передать необходимую информацию перед запуском потока. Последнее более эффективно, потому что код, выполняемый с использованием Synchronize, фактически выполняется в основном потоке, что снижает эффективность многопоточности.

Но на самом деле мое внимание привлекла первая строка: «загрузить исходный код веб-страницы в компонент памятки». Не делай этого! Не загружайте эти результаты в памятку для обработки. Автоматическую обработку лучше всего выполнять в памяти, за пределами визуального контроля. Использование строк, потоков или даже списков строк для обработки текста намного быстрее, чем использование заметок.
Список строк также имеет некоторые накладные расходы, но он использует ту же конструкцию индексации строк (TMemoStrings, которое является свойством Lines Memo, и TStringList имеют одного и того же предка), поэтому, если у вас есть код, который использует этого будет довольно легко преобразовать в TStringList.

person GolezTrol    schedule 06.11.2011
comment
+1 хороший подход, и спасибо, что указали на то, что использование заметок - плохая идея. Я нашел очень хороший потокобезопасный список строк, который может пригодиться здесь (TThreadStringList от Тило Эккерта): swissdelphicenter.ch/torry/showcode.php?id=2167 Это оболочка вокруг TStringList, которая использует критическую секцию для обеспечения безопасного доступа к нижележащему списку строк. - person Chris Thornton; 07.11.2011
comment
Действительно, удобно использовать TThreadStringList для списка элементов для загрузки. Каждый загруженный элемент можно поместить в отдельный TThreadStringList для обработки. Таким образом, вы можете разделить загрузку и обработку, как я предлагал, без особых хлопот. - person GolezTrol; 07.11.2011
comment
Спасибо за то, что не использовали памятные советы, я использую tstringslist для исходного текста веб-страницы, я новичок в области потоков Delphi, поэтому я полностью понимаю, что вы думаете, но мне потребуется некоторое время, чтобы я смог кодировать это , Я использую Google для получения сайтов, и через некоторое время он блокирует мои запросы (защита от ботов), поэтому одновременная загрузка и парсинг для меня неумная идея. Спасибо за Ваш ответ... - person Danijel Maksimovic Maxa; 07.11.2011
comment
К вашему сведению, у Indy есть свой собственный класс TIdThreadSafeStringList. Посмотрите на различные классы, доступные в модуле IdThreadSafe.pas. - person Remy Lebeau; 07.11.2011
comment
@Danijel: если Google отмечает вас, потому что вы слишком часто загружаете данные, просто замедлите частоту запуска кода загрузки. У Google нет возможности узнать, насколько быстро вы проанализируете данные после их загрузки. - person Remy Lebeau; 07.11.2011

Я бы посоветовал выполнять ВСЕ синтаксический анализ в потоках, чтобы основной поток вообще не выполнял синтаксический анализ. Основной поток должен управлять только пользовательским интерфейсом. Не анализируйте HTML из TMemo, загружайте каждый поток в TStream или String, а затем выполняйте синтаксический анализ напрямую из них. Используйте TIdSync или TIdNotify для отправки результатов синтаксического анализа в пользовательский интерфейс для отображения (если важна скорость, используйте TIdNotify). Включение компонентов пользовательского интерфейса в логику синтаксического анализа замедлит его.

person Remy Lebeau    schedule 06.11.2011
comment
Если синтаксический анализатор не только выполняет синтаксический анализ, но и выполняет некоторую обработку данных, он может быть не на 100% многопоточным. ИМХО парсинг будет намного быстрее скачивания. - person Arnaud Bouchez; 07.11.2011
comment
Это просто загрузка, синтаксический анализ и замена строки для замены% 20,% 3D и т. Д. В списке ... - person Danijel Maksimovic Maxa; 07.11.2011
comment
Все это можно сделать без привлечения пользовательского интерфейса, пока окончательный результат не будет готов для отображения. Соберите URL-адреса в TStringList, создайте потоки по мере необходимости для записей списка, где каждый поток загружается в String или TMemoryStream (TIdHTTP поддерживает оба), анализирует данные и отправляет результат в основной поток с помощью TIdNotify. Это потокобезопасно и позволяет избежать ненужных узких мест пользовательского интерфейса. - person Remy Lebeau; 07.11.2011
comment
Я новичок, два дня назад начал читать о темах, это то, что я умею и понимаю до сих пор [pastebin .com / bL0Ezfgu]. Любые хорошие примеры могут помочь мне лучше понять темы. Спасибо :) - person Danijel Maksimovic Maxa; 08.11.2011

Indy или Synapse готовы к многопоточности. Я бы рекомендовал использовать Synpase, который намного легче, чем Indy, и его будет достаточно для ваших целей. Не забывайте о HTTP API, предоставляемых Microsoft.

Простая реализация:

  • Один поток на URI;
  • Каждый поток получает данные, используя одно соединение HTTP;
  • Затем каждый поток анализирует данные;
  • Затем используйте Synchronize, чтобы обновить пользовательский интерфейс.

Пожалуй, мой любимый:

  • Определите максимальное количество используемых потоков (например, 8);
  • Каждый из этих потоков будет поддерживать удаленное соединение (это цель HTTP / 1.1 и действительно может повлиять на скорость);
  • Все запросы извлекаются этими потоками один за другим - не назначайте URL-адреса заранее потокам, но извлекайте новый URL-адрес из глобального списка, когда поток завершил его (каждый URL-адрес не всегда занимает одно и то же время);
  • Потоки могут ждать, пока любой другой URI не будет добавлен в глобальный список (например, используя Sleep(100) или семафор);
  • Затем проанализируйте и обновите пользовательский интерфейс в основном потоке графического интерфейса, используя специальное сообщение GDI (WM_USER+...) - анализ будет быстрым IMHO (и помните, что обновление пользовательского интерфейса может быть медленным - взгляните, например, на BeginUpdate-EndUpdate методы) - я обнаружил, что сообщение GDI (со связанными данными HTML) более эффективно, чем использование Synchronize, которое блокирует фоновый поток;
  • Другой вариант - выполнить синтаксический анализ в фоновом потоке сразу после получения данных из его URI - возможно, того не стоит (только если ваш синтаксический анализатор медленный), и вы можете столкнуться с проблемами многопоточности, если ваш синтаксический анализатор / процессор данных не на 100% потокобезопасен.

Во-вторых, как реализованы популярные так называемые «менеджеры загрузки».

Когда вы имеете дело с многопоточностью, вам придется «защищать» ваши общие ресурсы (например, списки). Используйте TCriticalSection для доступа к любому глобальному списку (например, списку URI) и снимите блокировку как можно скорее.

И попробуйте протестировать свою реализацию с несколькими компьютерами и сетями, с одновременным доступом, с различными операционными системами. Отладка многопоточных приложений может быть сложной, поэтому чем проще реализация, тем лучше: вот почему я рекомендую сделать часть загрузки многопоточной, но пусть основной поток обрабатывает данные (которые не будут огромными, поэтому он должен быть быстрым).

person Arnaud Bouchez    schedule 07.11.2011
comment
Можете ли вы предоставить мне простой код, как получить URL-адрес из списка, потому что я не знаю, как разделить 100 URL-адресов из списка на 8 потоков .... Я могу сделать ссылку на переменную и отправить ее в поток до thread.resume, но как это сделать дайте ссылку после того, как я начал, спасибо - person Danijel Maksimovic Maxa; 10.11.2011
comment
@DanijelMaksimovicMaxa URI - это просто глобальный TStringList, который читается из каждого потока, когда можно бесплатно загрузить новый файл. Вы не назначаете URI потокам, но вы позволяете потоку запрашивать список о любых оставшихся URI для загрузки. Вы должны защитить доступ к списку с помощью TCriticalSection, чтобы избежать одновременного получения одним и тем же URI двумя потоками. - person Arnaud Bouchez; 27.01.2012