Пишу многозадачную прогу. Нужна помощь!

  • Автор темы Stas_K
  • Дата начала
Stas_K

Stas_K

Участник
Регистрация
24.01.2006
Сообщения
924
Реакции
2
Баллы
18
Вопрос к программерам.

Нужно написать парсер интернет-страниц в несколько потоков.
Пишу на Delphi.
Поскольку с многозадачностью сталкиваюсь впервые, есть несколько вопросов.

С закачкой страницы в отдельном потоке я с горем пополам разобрался (TThread для организации потока и TIdHttp для запроса страницы).

Проблема в следующем:
После того, как поток получил ответ на запрос, он должен как-то передать в головной процесс информацию об этом. Как это сделать?
Можно ли в головном процессе обрабатывать какое-нибудь виндовое событие, которое происходит при завершении работы потока?
Или тупо гонять цикл и опрашивать созданные потоки?
 
Nusferatus

Nusferatus

Super Moderator
Регистрация
01.12.2006
Сообщения
40 161
Реакции
179
Баллы
63
Вопрос к программерам.

Нужно написать парсер интернет-страниц в несколько потоков.
Пишу на Delphi.
Поскольку с многозадачностью сталкиваюсь впервые, есть несколько вопросов.

С закачкой страницы в отдельном потоке я с горем пополам разобрался (TThread для организации потока и TIdHttp для запроса страницы).

Проблема в следующем:
После того, как поток получил ответ на запрос, он должен как-то передать в головной процесс информацию об этом. Как это сделать?
Можно ли в головном процессе обрабатывать какое-нибудь виндовое событие, которое происходит при завершении работы потока?
Или тупо гонять цикл и опрашивать созданные потоки?

Через очередь сообщений приложения попробуй.
Я в дэльфи мало чего понимаю - помню смутно лишь по школе, но на си там проще простого..
 
Nusferatus

Nusferatus

Super Moderator
Регистрация
01.12.2006
Сообщения
40 161
Реакции
179
Баллы
63
Вот как-то так ты его инициализируешь:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // указатель на атрибуты
DWORD dwStackSize, // размер стека потока в байтах
LPTHREAD_START_ROUTINE lpStartAddress, // указатель на функцию потока
LPVOID lpParameter, // аргумент для нового потока
DWORD dwCreationFlags, // флаг создания
LPDWORD lpThreadId // указатель на возвращаемый номер потока
);

Далее, твой поток выполняется, пока не произошло одно из следующих событий:
1. Поток вызвал одну из функций: ExitThread, ExitProcess, TerminateThread или TerminateProcess
2. Завершился цикл и произошел выход из функции (return).

Функция GetExitCodeThread возвращает статус завершения потока. Пока поток выполняется, он имеет статус завершения STILL_ACTIVE, при завершении он меняется на код выхода из потока.

Собсно по ней ты и можешь в основном процессе выяснить чем закончилась байда с твоим потоком.
Либо анализируй в цикле где-нить значение хэндла твоего потока. Если не NULL, значь всё ещё вертится.
Так же никто ен отменял общение между потоками и головным процессом при помощи глобальных переменных.
Ну и как я сказал - при помощи очереди сообщений приложения.
Посылать их можно функцией Win32 API SendMessage(...), при этом естессно использовать не забронированные системой идентификаторы, а WM_USER + index. Далее любую инфу ты можешь передавать через lParam. В общем масса вариантов.
 
Nusferatus

Nusferatus

Super Moderator
Регистрация
01.12.2006
Сообщения
40 161
Реакции
179
Баллы
63
ЗЫ. Кстати в winapi есть ещё такая прикольная функция GetThreadTimes, которая возвращает время, затраченное процессором на обработку указанного потока))
 
Nusferatus

Nusferatus

Super Moderator
Регистрация
01.12.2006
Сообщения
40 161
Реакции
179
Баллы
63
Ещё вспомнил.. При помощи WaitForSingleObject можно подождать завершения потока в основном цикле обработки сообщений процесса.
 
OP
Stas_K

Stas_K

Участник
Регистрация
24.01.2006
Сообщения
924
Реакции
2
Баллы
18
Ещё вспомнил.. При помощи WaitForSingleObject можно подождать завершения потока в основном цикле обработки сообщений процесса.

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

Nusferatus

Super Moderator
Регистрация
01.12.2006
Сообщения
40 161
Реакции
179
Баллы
63
Вот ждать-то как раз не надо.
Нужно, чтобы в момент завершения работы потока (то есть, когда получена запрашиваемая интернет-страница) головной процесс сразу же полученную страницу обработал и запустил запрос следующей страницы.

См. выше. 3 способа передачи данных между потоками.
Я использую всегда глобальные переменные в таком случае ибо это удобнее.
Их можно использовать даже при вызове хука из длл, хотя в таком случае гораздо сподручнее передавать любую структуру через Lparam.

Матчасть по данной теме можно покурить на rzdn.ru. Там и форумы есть.
ЗЫ. Дельфа дельфой, но win32 api никто не отменял ;)
 
Последнее редактирование:
OP
Stas_K

Stas_K

Участник
Регистрация
24.01.2006
Сообщения
924
Реакции
2
Баллы
18
Нашел более цивилизованный метод!
В потоке создал событие OnComplete, которое в головной программе привязал к процедуре. Работает! :)
SendMessage - тоже вариант, но не хочется заморачиваться...
 
Nusferatus

Nusferatus

Super Moderator
Регистрация
01.12.2006
Сообщения
40 161
Реакции
179
Баллы
63
Нашел более цивилизованный метод!
В потоке создал событие OnComplete, которое в головной программе привязал к процедуре. Работает! :)
SendMessage - тоже вариант, но не хочется заморачиваться...

Ыыы))) Вапщето теже яйца, вид сбоку)))
 
М

Модуль Оверлеев

Новичок
Регистрация
11.02.2005
Сообщения
1 186
Реакции
1
Баллы
0
рискну предположить, что твой OnComplete, в лучшем случае, свойство типа TNotifyEvent. или просто член класса. при завершении, поток вызывает этот делегат(сори за плюсовую терминологию), который, к примеру, добавляет в како-нить контейнер полученные данные. успешность этой операции сильно зависит от начального дезайна этой архитектуры. чтобы все работало без объектов синхронизации, можно было бы сделать примерно так(я почти не думал над этим, задача слишком обстрактная): какой-нить главный поток создает массив указателей на данные, к примеру с N ячейками. создает N потоков, указывая им, какие сайты они должны скачать, и в какие ячейки положить указатели на полученные данные. каждый рабочий поток, когда скачает, разместит в памяти, и сделает всю работу, записывает указатель в массив. главный поток спит (Sleep())циклически проверяя, что все ячейки массива перестали быть nil. когда они перестали, он обрабатывает эти данные, и потом освобождает память.
недостатки этого подхода:
1 - главный. можно гарантировать, что без синхронизации разложение (corruption) данных не произойдет, на 32-битной платформе Intel, где массив указателей выровнен на 4-байта. операция записи такого указателя будет атомарной.
достоинства:
1 - сомнительное. не использованы объекты синхронизации.

короче, я бы так делать не стал. для винды существует очень быстрая и легковесная критическая секция, которая прекрасно синхрит потоки в одном процессе.
 
Nusferatus

Nusferatus

Super Moderator
Регистрация
01.12.2006
Сообщения
40 161
Реакции
179
Баллы
63
рискну предположить, что твой OnComplete, в лучшем случае, свойство типа TNotifyEvent. или просто член класса. при завершении, поток вызывает этот делегат(сори за плюсовую терминологию), который, к примеру, добавляет в како-нить контейнер полученные данные. успешность этой операции сильно зависит от начального дезайна этой архитектуры. чтобы все работало без объектов синхронизации, можно было бы сделать примерно так(я почти не думал над этим, задача слишком обстрактная): какой-нить главный поток создает массив указателей на данные, к примеру с N ячейками. создает N потоков, указывая им, какие сайты они должны скачать, и в какие ячейки положить указатели на полученные данные. каждый рабочий поток, когда скачает, разместит в памяти, и сделает всю работу, записывает указатель в массив. главный поток спит (Sleep())циклически проверяя, что все ячейки массива перестали быть nil. когда они перестали, он обрабатывает эти данные, и потом освобождает память.
недостатки этого подхода:
1 - главный. можно гарантировать, что без синхронизации разложение (corruption) данных не произойдет, на 32-битной платформе Intel, где массив указателей выровнен на 4-байта. операция записи такого указателя будет атомарной.
достоинства:
1 - сомнительное. не использованы объекты синхронизации.

короче, я бы так делать не стал. для винды существует очень быстрая и легковесная критическая секция, которая прекрасно синхрит потоки в одном процессе.

+1
Всё верно.
Щас интереса ради залез в азбуку дельфей, поглазеть как там что реализовано с потоками)) Одно скажу ребята: C++ рулид!)))
 
OP
Stas_K

Stas_K

Участник
Регистрация
24.01.2006
Сообщения
924
Реакции
2
Баллы
18
Одно скажу ребята: C++ рулид!)))

Нет времени с C++ разбираться. Да и на Delphi насобачился уже.

не использованы объекты синхронизации.

Я не совсем разобрался в потоках, но синхронизация есть. Собственно, результате синхронизации событие и происходит (если не ошибаюсь).
Вот так в модуле потока все выглядит:

procedure TDownloadThread.Execute;
begin
try
IdHttp:=TIdHttp.Create(nil);
IdHttp.AllowCookies:=True;
IdHttp.AuthRetries:=5;
IdHttp.HandleRedirects:=False;
IdHttp.Port:=80;
IdHttp.RecvBufferSize:=65534;
IdHttp.Request.Accept:='text/html';
IdHttp.ProxyParams.ProxyServer:=ProxyServer;
IdHttp.ProxyParams.ProxyPort:=ProxyPort;
QueryResult:=IdHttp.Get(QueryString);
finally
Synchronize(FComplete);
IdHttp.DisconnectSocket;
IdHttp.Free;
end;
end;

procedure TDownloadThread.FComplete;
begin
if Assigned(FOnComplete) then FOnComplete(Self);
end;

Переменные, которые передаются в головной процесс, объявлены в секции public, так что при выполнении процедуры в головном процессе их видно.

Я не претендую на звание хорошего программиста - меня этому не учили. Но работает же!
 
М

Модуль Оверлеев

Новичок
Регистрация
11.02.2005
Сообщения
1 186
Реакции
1
Баллы
0
уже не помню, как Synchronize() устроен, но, думаю, что этого достаточно...
 
Vivos

Vivos

Новичок
Регистрация
04.07.2004
Сообщения
6 411
Реакции
1
Баллы
0
Тут же напишу... Разрабатываю прогу для коммерческой разведки... Сейчас думаю над модулем поиска информацию по средством поисковиков... Как научить программу переходить по ссылкам??? И как обнаруживать нужную информацию на странице? Ссылки и текст? Программер из меня еще тот...
 
ScorpionSP

ScorpionSP

Участник
Регистрация
14.09.2006
Сообщения
6 023
Реакции
9
Баллы
38
Их можно использовать даже при вызове хука из длл, хотя в таком случае гораздо сподручнее передавать любую структуру через Lparam.
В С++ с этим осторожнее, тут есть грабли, связанные с пробросом STLных контейнеров в DLL.

Матчасть по данной теме можно покурить на rzdn.ru. Там и форумы есть.
Ну только rsnd.ru. Еще CodeProject и Google Groups рулят.

ЗЫ. Дельфа дельфой, но win32 api никто не отменял ;)
+1.
 
ScorpionSP

ScorpionSP

Участник
Регистрация
14.09.2006
Сообщения
6 023
Реакции
9
Баллы
38
+1
Всё верно.
Щас интереса ради залез в азбуку дельфей, поглазеть как там что реализовано с потоками)) Одно скажу ребята: C++ рулид!)))
Всё-таки в таких вещах, что топикстартеру нужно додиез рулит.
 
ScorpionSP

ScorpionSP

Участник
Регистрация
14.09.2006
Сообщения
6 023
Реакции
9
Баллы
38
рискну предположить, что твой OnComplete, в лучшем случае, свойство типа TNotifyEvent. или просто член класса. при завершении, поток вызывает этот делегат(сори за плюсовую терминологию), который, к примеру, добавляет в како-нить контейнер полученные данные. успешность этой операции сильно зависит от начального дезайна этой архитектуры. чтобы все работало без объектов синхронизации, можно было бы сделать примерно так(я почти не думал над этим, задача слишком обстрактная): какой-нить главный поток создает массив указателей на данные, к примеру с N ячейками. создает N потоков, указывая им, какие сайты они должны скачать, и в какие ячейки положить указатели на полученные данные. каждый рабочий поток, когда скачает, разместит в памяти, и сделает всю работу, записывает указатель в массив. главный поток спит (Sleep())циклически проверяя, что все ячейки массива перестали быть nil. когда они перестали, он обрабатывает эти данные, и потом освобождает память.
недостатки этого подхода:
1 - главный. можно гарантировать, что без синхронизации разложение (corruption) данных не произойдет, на 32-битной платформе Intel, где массив указателей выровнен на 4-байта. операция записи такого указателя будет атомарной.
достоинства:
1 - сомнительное. не использованы объекты синхронизации.

короче, я бы так делать не стал. для винды существует очень быстрая и легковесная критическая секция, которая прекрасно синхрит потоки в одном процессе.
Что-то тут по-мойму нужен WaitForMultipleObjects просто.
Главное, в дедлок не загнать :)
 
SWW

SWW

Участник
Регистрация
14.11.2006
Сообщения
257
Реакции
0
Баллы
16
А на профильных форумах не пробовал спрашивать? :) кывт например...
 
Верх Низ