2011-11-06 1 views
0

Каков самый быстрый способ загрузить источник веб-страницы в компонент напоминания? Я использую компоненты 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?

+0

Я бы посоветовал вам прочитать документацию о том, что делает Synchronize, и заставить каждый поток запрашивать один (и только один) URL-адрес при его запуске и каждый раз после этого обрабатывает один URL-адрес. Если веб-сайты используют XHTML, я бы также проверил метод DOMDocument.load MSXML2_TLB, чтобы проверить, работает ли загрузка и синтаксический анализ. –

ответ

9

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

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

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

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

+2

Downvoter, пожалуйста, мотивируйте то, что вы считаете неправильным? – GolezTrol

+0

+1 приятный подход, и спасибо, что указали, что использование memo control - плохая идея. Я нашел очень хороший потокобезопасный строковый список, который был бы здесь полезен (TThreadStringList by Tilo Eckert): http://www.swissdelphicenter.ch/torry/showcode.php?id=2167 Это оболочка вокруг TStringList который использует критический раздел для обеспечения безопасного доступа к основному списку строк. –

+0

Это действительно удобно использовать TThreadStringList для списка элементов для загрузки. Каждый загруженный элемент может быть перенесен в отдельный TThreadStringList для обработки. Таким образом, вы можете разделить загрузку и обработку, как я предлагал, без лишних хлопот. – GolezTrol

5

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

+1

Если анализатор не просто разбирается, но выполняет некоторую обработку данных, он может быть не 100% многопоточным. Анализ IMHO будет намного быстрее, чем загрузка. –

+0

Это просто загрузка и разбор, и строка для замены% 20,% 3D и т. Д. ... в списке ... –

+0

Все это можно сделать без привлечения пользовательского интерфейса, пока окончательный результат не будет готов к отображению. Соберите URL-адреса в TStringList, создайте потоки, необходимые для записей в списке, где каждый поток загружается в String или TMemoryStream (TIdHTTP поддерживает оба), анализирует данные и отправляет результат в основной поток с помощью TIdNotify. Это поточно-безопасное и избегает ненужных узких мест в пользовательском интерфейсе. –

4

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

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

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

Может быть, моя любимая:

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

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

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

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

+0

Можете ли вы предоставить мне простой код, как извлечь URL из списка, потому что я не знаю, как разделить 100 URL-адресов из списка на 8 потоков .... Я могу сделать ссылку переменной и отправить ее в поток до thread.resume, но как чтобы дать ему ссылку после того, как я начал ее, спасибо –

+1

@DanijelMaksimovicMaxa URI - это всего лишь глобальный 'TStringList', который читается из каждого потока, когда он доступен для загрузки нового файла. Вы * не * назначаете URI для потоков, но вы позволяете нитку запрашивать список о любом оставшемся URI для загрузки. Вы должны защитить доступ к списку с помощью TCriticalSection, чтобы избежать двух потоков, получающих один и тот же URI за один раз. –

Смежные вопросы