Уважаемые, встал в тупик и не знаю что делать, подскажите плз:
Есть задача: многопоточная обработка данных. В частности: необходимо многопоточно анализировать веб страницы. С этих страниц мы выдираем ссылки и их анализируем и т.д.
Как мне это видится:
1. Есть поток, который занимается только скачиванием страницы в нужное место для последующего поиска. По завершению потока выполняется некая функция OnFinishThread,
2. Есть некая функция PushPage(address), которая добавляет страницу в общий список, присваивает странице address состояние типа NEED_DOWNLOAD и все, больше она ничего не делает.
3. Есть некая функция Update, которая просматривает список страниц и для каждой страницы со статусом NEED_DOWNLOAD запускает тот самый поток
4. Функция OnFinishThread берет ту страницу, которую скачал соотв. вызывающий поток, и вызывает функцию ParsePage, которая на каждый найденный урл вызовет соотв. PushAddress и т.д.
Соотв. функция Update циклическая и все скачивается, анализируется и т.д.
Вопрос: с точки зрения многопоточности критичная для нас функция OnFinishThread, т.к. ее могут вызвать сразу несколько потоков одновременно, прально? Соотв. делаем, например, так:
void OnFinishThread(Tthread *Thread)
{
EnterCriticalSection(&CS);
try
{
if (Locker > 0) throw Exception("Херня, залезло слишком много потоков одновременно ");
++Locker;
try
{
ParsePage(Thread->Page);
}__finally
{
--Locker;
};
}__finally
{
LeaveCriticalSection(&CS);
};
};
Подобный вариант НЕ РАБОТАЕТ ((
Как подобное лучше сделать?!
Re: Многопоточность + синхронизация + как бороться?
Hello HAS, you wrote:
> Вопрос: с точки зрения многопоточности критичная для нас функция OnFinishThread, т.к. ее могут вызвать сразу несколько потоков одновременно, прально? Соотв. делаем, например, так: > > void OnFinishThread(Tthread *Thread) > { > EnterCriticalSection(&CS); > try > { > if (Locker > 0) throw Exception("Херня, залезло слишком много потоков одновременно ");
Данная проверка бессмысленна, т.к. EnterCriticalSection ГАРАНТИРУЕТ, что данный блок выполняется только одним потоком.
Что не работает?
Кроме того, критическую секцию нужно ставить не на всю функцию, а только на те места где идет обращение в разделяемым ресурсам, т.е. ресурсам к котором возможен доступ из нескольких потоков.
--
Всего хорошего, Слава
ICQ: 197577902
Posted via RSDN NNTP Server 2.0
Re[2]: Многопоточность + синхронизация + как бороться?
Здравствуйте, Slava Antonov, Вы писали:
SA>Hello HAS, you wrote:
>> Вопрос: с точки зрения многопоточности критичная для нас функция OnFinishThread, т.к. ее могут вызвать сразу несколько потоков одновременно, прально? Соотв. делаем, например, так: >> >> void OnFinishThread(Tthread *Thread) >> { >> EnterCriticalSection(&CS); >> try >> { >> if (Locker > 0) throw Exception("Херня, залезло слишком много потоков одновременно ");
SA>Данная проверка бессмысленна, т.к. EnterCriticalSection ГАРАНТИРУЕТ, что данный блок выполняется только одним потоком.
тогда почему срабатывает данное исключение?
SA>Что не работает?
вроде как работает, но: один раз может отработать без задоринки, в другой раз может просто выдать "out of memory", в третий — подвиснуть... причем пытался уже статически задавать последовательность страниц, та же история...
SA>Кроме того, критическую секцию нужно ставить не на всю функцию, а только на те места где идет обращение в разделяемым ресурсам, т.е. ресурсам к котором возможен доступ из нескольких потоков.
а как быть если мне необходимо, чтобы весь этот блок кода был выполнен без прерывания одним потоком, т.е. чтобы в этом блоке в ходе выполнения не было переключения между потоками?
SA>-- SA>Всего хорошего, Слава SA>ICQ: 197577902
Re[3]: Многопоточность + синхронизация + как бороться?
Hello HAS, you wrote:
>> Данная проверка бессмысленна, т.к. EnterCriticalSection ГАРАНТИРУЕТ, что данный блок выполняется только одним потоком. > тогда почему срабатывает данное исключение?
Какое исключение?
>> Что не работает? > вроде как работает, но: один раз может отработать без задоринки, в другой раз может просто выдать "out of memory", в третий — подвиснуть... причем пытался уже статически задавать последовательность страниц, та же история...
Это нужно смотреть что у вас в ParsePage(Thread->Page);
>> Кроме того, критическую секцию нужно ставить не на всю функцию, а только на те места где идет обращение в разделяемым ресурсам, т.е. ресурсам к котором возможен доступ из нескольких потоков. > а как быть если мне необходимо, чтобы весь этот блок кода был выполнен без прерывания одним потоком, т.е. чтобы в этом блоке в ходе выполнения не было переключения между потоками?
Зачем? Какой вам тогда вообще смысл в многопоточности если вы ее накорню прибиваете.
--
Всего хорошего, Слава
ICQ: 197577902
Posted via RSDN NNTP Server 2.0
Re[4]: Многопоточность + синхронизация + как бороться?
Здравствуйте, Slava Antonov, Вы писали:
SA>Hello HAS, you wrote:
>>> Данная проверка бессмысленна, т.к. EnterCriticalSection ГАРАНТИРУЕТ, что данный блок выполняется только одним потоком. >> тогда почему срабатывает данное исключение?
SA>Какое исключение?
>>> Что не работает? >> вроде как работает, но: один раз может отработать без задоринки, в другой раз может просто выдать "out of memory", в третий — подвиснуть... причем пытался уже статически задавать последовательность страниц, та же история...
SA>Это нужно смотреть что у вас в ParsePage(Thread->Page);
на данный момент там PushPage(Addresses[random(AddressCounts)]); т.е. просто добавление страницы в очередь
>>> Кроме того, критическую секцию нужно ставить не на всю функцию, а только на те места где идет обращение в разделяемым ресурсам, т.е. ресурсам к котором возможен доступ из нескольких потоков. >> а как быть если мне необходимо, чтобы весь этот блок кода был выполнен без прерывания одним потоком, т.е. чтобы в этом блоке в ходе выполнения не было переключения между потоками?
SA>Зачем? Какой вам тогда вообще смысл в многопоточности если вы ее накорню прибиваете.
почему? каждый поток по идее может занимать в работе длительное время, т.е. грубо аналог: Teleport Pro — какие-то страницы могут быть недоступны и поток будет ждать таймаута, в то вермя как другой поток в это время скачает другую страницу...
Чем же я прибиваю многопоточность?
SA>-- SA>Всего хорошего, Слава SA>ICQ: 197577902
Re: Многопоточность + синхронизация + как бороться?
On Sun, 05 Mar 2006 19:35:26 +0600, HAS <36274@users.rsdn.ru> wrote:
>> Это нужно смотреть что у вас в ParsePage(Thread->Page); > на данный момент там PushPage(Addresses[random(AddressCounts)]); т.е. просто добавление страницы в очередь
Хм... кстати, может менеджер памяти не знает про многопоточность и начинают лезть глюки с памятью?
По поводу зависаний: если зависло и при этом не есть процессорное время, то дело явно в deadlock.
В любом случае это нужно все прогнать под отладчиком.
>> Зачем? Какой вам тогда вообще смысл в многопоточности если вы ее накорню прибиваете. > почему?
Не, если функция состоит всего из одной простой операции, то все нормально. Но если там что-нить посложнее, то получится что остальные потоки вместо того чтобы делать свое дело будут тупо ждать пока функция завершит свою работу, что не есть хорошо.
Как я уже говорил, синхронизировать доступ нужно не к функциям, а к разделяемым ресурсам. В вашем случае — это та очередь с которой вы работаете.
--
Всего хорошего, Слава.
ICQ: 197577902
Posted via RSDN NNTP Server 2.0
Re: Многопоточность + синхронизация + как бороться?
Пока поток работает только с локальными данными и не обращается ни к каким глобальным данным (к которым одновременно обращаются сразу несколько потоков) никакая синхронизация не нужна
On Mon, 06 Mar 2006 09:10:15 +0600, remark <38267@users.rsdn.ru> wrote:
> Здесь вообще не нужна синхронизация, т.к. нет работы с разделяемыми ресурсами.
А как же параметр Thread?
Так что синхронизация нужна.
--
Всего хорошего, Слава.
ICQ: 197577902
Posted via RSDN NNTP Server 2.0
Re[3]: Многопоточность + синхронизация + как бороться?
Здравствуйте, npo_mir, Вы писали:
_>On Mon, 06 Mar 2006 09:10:15 +0600, remark <38267@users.rsdn.ru> wrote:
>> Здесь вообще не нужна синхронизация, т.к. нет работы с разделяемыми ресурсами.
_>А как же параметр Thread? _>Так что синхронизация нужна.
А кто сказал, что Thread — разделяемый ресурс? Во всяком случае нужно смотреть реализацию из которой это будет видно. А вообще судя по описаной логике, разделяемый ресурс — это очередь куда складываются и откуда достаются готовые, скачанные страницы. Вот обращения к ней push и pop нужно синхронизировать. В остальном синхронизация не нужна, пусть себе работают потоки (каждый со своим ресурсом) и сигналят о готовности.
Вообще я себе представляю задачу так:
1. Стартуем поток, которому передаем параметр — какую страницу качать.
2. По завершении скачивания поток синхронно закидывает готовую страницу в очередь, сигналит о своем завершении=появлении новой страницы в очереди и завершается (как вариант не завершается, а берет из очереди заданий новую страницу которую нужно качать)
3. Поток-монитор получив сигнал, синхронно забирает из очереди готовую страницу и дальше делает с ней что угодно, больше кроме него эту страницу никто не трогает.
Вот на мой взгляд те две точки (выделенные жирным цветом ), которые нужно синхронизировать.
Re: Многопоточность + синхронизация + как бороться?
HAS>Уважаемые, встал в тупик и не знаю что делать, подскажите плз:
HAS>Вопрос: с точки зрения многопоточности критичная для нас функция OnFinishThread, т.к. ее могут вызвать сразу несколько потоков одновременно, прально? Соотв. делаем, например, так:
Здравствуйте, npo_mir, Вы писали:
_>On Sun, 05 Mar 2006 19:35:26 +0600, HAS <36274@users.rsdn.ru> wrote:
>>> Это нужно смотреть что у вас в ParsePage(Thread->Page); >> на данный момент там PushPage(Addresses[random(AddressCounts)]); т.е. просто добавление страницы в очередь
_>Хм... кстати, может менеджер памяти не знает про многопоточность и начинают лезть глюки с памятью?
по-идее должен знать... пишу всо под Builder 5.0... кстати, очень часто проект подвисает с ошибкой типа "что-то там сломалось в Borlandbk50.bpk". За что отвесает этот пакет?
и еще: какие моменты не может уловить CodeGuard? потому как с точки зрения кода — везде все очищается как надо, но если забить большой цикл по созданию и убиванию
этого менеджера (порядка > 1000) то память жрется неплохо, а CodeGuard молчит ((
_>По поводу зависаний: если зависло и при этом не есть процессорное время, то дело явно в deadlock.
_>В любом случае это нужно все прогнать под отладчиком.
заманался гонять, такое ощущение, что отладчик глюкаво работает, потому как очень часто выкидывает на код, где в принципе ничего быть не должно (там даже многопоточности нету)
>>> Зачем? Какой вам тогда вообще смысл в многопоточности если вы ее накорню прибиваете. >> почему?
_>Не, если функция состоит всего из одной простой операции, то все нормально. Но если там что-нить посложнее, то получится что остальные потоки вместо того чтобы делать свое дело будут тупо ждать пока функция завершит свою работу, что не есть хорошо.
поток только качает страницу и скидывает её в файлик
_>Как я уже говорил, синхронизировать доступ нужно не к функциям, а к разделяемым ресурсам. В вашем случае — это та очередь с которой вы работаете.
---
при этом DownloadPage создает и запускает поток, ParsePage — выдирает из страницы ссылки и добавляет их в очередь через PushPage (pushpage заключен между EnterCriticalSection & LeaveCriticalSection). Чтоздесь не так?
_>-- _>Всего хорошего, Слава. _>ICQ: 197577902
Re[2]: Многопоточность + синхронизация + как бороться?
Здравствуйте, Ligen, Вы писали:
L><skipped>
HAS>>Подобный вариант НЕ РАБОТАЕТ ((
HAS>>Как подобное лучше сделать?!
L>Ты ее инициализируешь? Покажи весь код
инициализация происходит в конструкторе менеджера, а убиение соотв. в деструкторе...
L>См. MSDN::InitializeCriticalSection — может выбрасывать exception
ну тогда наверное и менеджер не стал бы работать? а он работает... вылетает просто от балды (
L>З.Ы. Оберни нативную CS врапперами, замучаешься везде писать try/catch
типа чтобы юзать так:
void ....
{
TCSWrapper Wrapper(&CS); // в конструкторе EnterCriticalSection
...
// а здесь типа срабатывает деструктор с LeaveCriticalSection
};
??? Есть гарантия 100% что деструктор всегда будет отрабатывать во время и верно?
Re[2]: Многопоточность + синхронизация + как бороться?
R>Пока поток работает только с локальными данными и не обращается ни к каким глобальным данным (к которым одновременно обращаются сразу несколько потоков) никакая синхронизация не нужна
так Thread->Page и есть глобальные данные, с которыми происходит работа в нескольких местах программы
R>
Re[4]: Многопоточность + синхронизация + как бороться?
Здравствуйте, Alexey Frolov, Вы писали:
AF>Здравствуйте, npo_mir, Вы писали:
_>>On Mon, 06 Mar 2006 09:10:15 +0600, remark <38267@users.rsdn.ru> wrote:
>>> Здесь вообще не нужна синхронизация, т.к. нет работы с разделяемыми ресурсами.
_>>А как же параметр Thread? _>>Так что синхронизация нужна.
AF>А кто сказал, что Thread — разделяемый ресурс? Во всяком случае нужно смотреть реализацию из которой это будет видно. А вообще судя по описаной логике, разделяемый ресурс — это очередь куда складываются и откуда достаются готовые, скачанные страницы. Вот обращения к ней push и pop нужно синхронизировать. В остальном синхронизация не нужна, пусть себе работают потоки (каждый со своим ресурсом) и сигналят о готовности.
так и есть... при этом как такого pop'а нету, есть Update, который пробегает по всей очереди и в зависимости от статуса страницы либо вызывает ParsePage, либо PushPage, либо создает новый поток
AF>Вообще я себе представляю задачу так: AF>1. Стартуем поток, которому передаем параметр — какую страницу качать. AF>2. По завершении скачивания поток синхронно закидывает готовую страницу в очередь, сигналит о своем завершении=появлении новой страницы в очереди и завершается (как вариант не завершается, а берет из очереди заданий новую страницу которую нужно качать)
что значит "синхронно закидывает страницу в очередь" ? т.е. в рамках блока EnterCriticalSection & LeaveeCriticalSection ? или как? и как лучше отсигналить?
AF>3. Поток-монитор получив сигнал, синхронно забирает из очереди готовую страницу и дальше делает с ней что угодно, больше кроме него эту страницу никто не трогает.
поток монитор получается основной поток... опять же что значит синхронно?
AF>Вот на мой взгляд те две точки (выделенные жирным цветом ), которые нужно синхронизировать.
в принципе так все и сделано:
1. есть update, который бегает по очереди и в зависимости от статуса страницы либо разбирает её, либо запускает новый поток
2. есть OnFinishthread, который вызывается по завершению любого потока, скачавшего страницу. он меняет статус страницы на скачанный (в рамках EnterCS & LeaveSC) и вызывает update
3. есть pushpage, который просто добавляет в конец очереди новую страницу
4. есть parsepage, который вызывается только из update. Он выдирает ссылки и для каждой ссылки вызывает pushpage
где ошибка в логике/архитектуре?
Re[2]: Многопоточность + синхронизация + как бороться?
HAS>>Уважаемые, встал в тупик и не знаю что делать, подскажите плз:
HAS>>Вопрос: с точки зрения многопоточности критичная для нас функция OnFinishThread, т.к. ее могут вызвать сразу несколько потоков одновременно, прально? Соотв. делаем, например, так:
K>Ведь при входе в OnFinishThread можно выставить семафор, а перед вызовом OnFinishThread его проверять и оценивать необходимость запуска.
да, можно. В принципе можно использовать любую методику синхронизации... Мне важно другое: с точки зрения синхронизации здесь вроде все правильно. Соотв. мне ВАЖНО ПОНЯТЬ, где что и почему не работает, дабы не наступать на эти же грабли в будущем!
Re[5]: Многопоточность + синхронизация + как бороться?
Hello HAS, you wrote:
> в принципе так все и сделано: > 1. есть update, который бегает по очереди и в зависимости от статуса страницы либо разбирает её, либо запускает новый поток > 2. есть OnFinishthread, который вызывается по завершению любого потока, скачавшего страницу. он меняет статус страницы на скачанный (в рамках EnterCS & LeaveSC) и вызывает update > 3. есть pushpage, который просто добавляет в конец очереди новую страницу > 4. есть parsepage, который вызывается только из update. Он выдирает ссылки и для каждой ссылки вызывает pushpage > > где ошибка в логике/архитектуре?
В логике ошибок не замечено (при условии что везде производится синхронизированный доступ к одереди). Остаются ошибки реализации.
--
Всего хорошего, Слава
ICQ: 197577902
Posted via RSDN NNTP Server 2.0
Re[7]: Многопоточность + синхронизация + как бороться?
Здравствуйте, HAS, Вы писали:
HAS>Уважаемые, встал в тупик и не знаю что делать, подскажите плз:
HAS>Есть задача: многопоточная обработка данных. В частности: необходимо многопоточно анализировать веб страницы. С этих страниц мы выдираем ссылки и их анализируем и т.д.
HAS>Как мне это видится: HAS>1. Есть поток, который занимается только скачиванием страницы в нужное место для последующего поиска. По завершению потока выполняется некая функция OnFinishThread, HAS>2. Есть некая функция PushPage(address), которая добавляет страницу в общий список, присваивает странице address состояние типа NEED_DOWNLOAD и все, больше она ничего не делает. HAS>3. Есть некая функция Update, которая просматривает список страниц и для каждой страницы со статусом NEED_DOWNLOAD запускает тот самый поток HAS>4. Функция OnFinishThread берет ту страницу, которую скачал соотв. вызывающий поток, и вызывает функцию ParsePage, которая на каждый найденный урл вызовет соотв. PushAddress и т.д. HAS>Соотв. функция Update циклическая и все скачивается, анализируется и т.д.
Вообще говоря, запускать на каждый адрес по потоку — это не лучшее решение. Легко можно попасть в ситуацию, когда будут одновременно запущены сотни и тысячи потоков, что приведет к перерасходу системных ресурсов (запуск потока — не очень дешевая операция, и синхронизация тысяч потоков для системы тоже дорога). Кроме того, будут еще и инициированы одновременно тысячи соединений (скачивание страниц), что тоже не есть хорошо.
Лучшим будет решение с пулом потоков, каждый из которых в цикле выбирает адреса страниц из глобального списка, скачивает страницу и анализирует результат. Соответственно, синхронизация нужна только при доступе к этому списку, ну и при помещении результата куда надо, конечно. Количество потоков легко регулировать в соответствии с загрузкой системой и количеством процессоров. Вариант — два пула потоков, один скачивает страницы, другой обрабатывает. Примерно так работают веб-сервера.
Re[8]: Многопоточность + синхронизация + как бороться?
Здравствуйте, Slava Antonov, Вы писали:
SA>Hello HAS, you wrote:
>> заманался гонять, такое ощущение, что отладчик глюкаво работает
SA>Маловероятно.
>> потому как очень часто выкидывает на код, где в принципе ничего быть не должно (там даже многопоточности нету)
SA>Видимо в каком-то месте произошла порча памяти и что-нить подобное. Какая ошибка при этом возникает? AV?
либо Исключение, либо просто выбрасывает в окно CPU Debug...
п.с. а что такое AV ?
SA>-- SA>Всего хорошего, Слава SA>ICQ: 197577902
Re[6]: Многопоточность + синхронизация + как бороться?
Здравствуйте, Slava Antonov, Вы писали:
SA>Hello HAS, you wrote:
>> в принципе так все и сделано: >> 1. есть update, который бегает по очереди и в зависимости от статуса страницы либо разбирает её, либо запускает новый поток >> 2. есть OnFinishthread, который вызывается по завершению любого потока, скачавшего страницу. он меняет статус страницы на скачанный (в рамках EnterCS & LeaveSC) и вызывает update >> 3. есть pushpage, который просто добавляет в конец очереди новую страницу >> 4. есть parsepage, который вызывается только из update. Он выдирает ссылки и для каждой ссылки вызывает pushpage >> >> где ошибка в логике/архитектуре?
SA>В логике ошибок не замечено (при условии что везде производится синхронизированный доступ к одереди). Остаются ошибки реализации.
хм... скажем, если все процедуры обернуть в EnterCS & LeaveCS, то доступ должен быть синхронизирован, правильно? — пусть даже в ущерб многопоточности... Пробовал и так, все по прежнему
SA>-- SA>Всего хорошего, Слава SA>ICQ: 197577902
Re[2]: Многопоточность + синхронизация + как бороться?
Здравствуйте, Vadim B, Вы писали:
VB>Здравствуйте, HAS, Вы писали:
HAS>>Уважаемые, встал в тупик и не знаю что делать, подскажите плз:
HAS>>Есть задача: многопоточная обработка данных. В частности: необходимо многопоточно анализировать веб страницы. С этих страниц мы выдираем ссылки и их анализируем и т.д.
HAS>>Как мне это видится: HAS>>1. Есть поток, который занимается только скачиванием страницы в нужное место для последующего поиска. По завершению потока выполняется некая функция OnFinishThread, HAS>>2. Есть некая функция PushPage(address), которая добавляет страницу в общий список, присваивает странице address состояние типа NEED_DOWNLOAD и все, больше она ничего не делает. HAS>>3. Есть некая функция Update, которая просматривает список страниц и для каждой страницы со статусом NEED_DOWNLOAD запускает тот самый поток HAS>>4. Функция OnFinishThread берет ту страницу, которую скачал соотв. вызывающий поток, и вызывает функцию ParsePage, которая на каждый найденный урл вызовет соотв. PushAddress и т.д. HAS>>Соотв. функция Update циклическая и все скачивается, анализируется и т.д.
VB>Вообще говоря, запускать на каждый адрес по потоку — это не лучшее решение. Легко можно попасть в ситуацию, когда будут одновременно запущены сотни и тысячи потоков, что приведет к перерасходу системных ресурсов (запуск потока — не очень дешевая операция, и синхронизация тысяч потоков для системы тоже дорога). Кроме того, будут еще и инициированы одновременно тысячи соединений (скачивание страниц), что тоже не есть хорошо.
количество потоков я ограничиваю некой величиной MAXTHREAD ( <= 10), т.е. если в работе MAXTHREAD потоков, то другие мы не запускаем
VB>Лучшим будет решение с пулом потоков, каждый из которых в цикле выбирает адреса страниц из глобального списка, скачивает страницу и анализирует результат. Соответственно, синхронизация нужна только при доступе к этому списку, ну и при помещении результата куда надо, конечно. Количество потоков легко регулировать в соответствии с загрузкой системой и количеством процессоров. Вариант — два пула потоков, один скачивает страницы, другой обрабатывает. Примерно так работают веб-сервера.
не совсем понял что значит два пула потоков... как это выглядит (архитектурно — структурно)
Re[9]: Многопоточность + синхронизация + как бороться?
On Thu, 09 Mar 2006 15:38:40 +0600, HAS <36274@users.rsdn.ru> wrote:
> либо Исключение, либо просто выбрасывает в окно CPU Debug... > п.с. а что такое AV ?
Access violation — возникает при обращении к уже разрушенным объектам, по неверному адресу, и т.д. Т.е. явный признак ошибок работы программы с памятью.
--
Всего хорошего, Слава.
ICQ: 197577902
Posted via RSDN NNTP Server 2.0
Re[7]: Многопоточность + синхронизация + как бороться?
On Thu, 09 Mar 2006 15:40:35 +0600, HAS <36274@users.rsdn.ru> wrote:
> хм... скажем, если все процедуры обернуть в EnterCS & LeaveCS, то доступ должен быть синхронизирован, правильно?
Правильно.
> Пробовал и так, все по прежнему
Либо есть еще места где идет обращение. Либо ошибка в другом месте. как я уже говорил, например где-то происходит порча памяти.
--
Всего хорошего, Слава.
ICQ: 197577902
Posted via RSDN NNTP Server 2.0
Re[5]: Многопоточность + синхронизация + как бороться?
Здравствуйте, HAS, Вы писали:
AF>>А кто сказал, что Thread — разделяемый ресурс? Во всяком случае нужно смотреть реализацию из которой это будет видно. А вообще судя по описаной логике, разделяемый ресурс — это очередь куда складываются и откуда достаются готовые, скачанные страницы. Вот обращения к ней push и pop нужно синхронизировать. В остальном синхронизация не нужна, пусть себе работают потоки (каждый со своим ресурсом) и сигналят о готовности.
HAS>так и есть... при этом как такого pop'а нету, есть Update, который пробегает по всей очереди и в зависимости от статуса страницы либо вызывает ParsePage, либо PushPage, либо создает новый поток
В данном конкретном случае Update проверяет статус страницы, значит кто-то его может менять, следовательно и проверять и обновлять этот статус нужно синхронно, т.е обеспечить невозможность одновременного доступа разных потоков к переменной отображающей статус страницы. Делать это можно либо оборачивая соответствующие участки кода в enter-leave critical section, либо с помощью interlocked... функций.
HAS>что значит "синхронно закидывает страницу в очередь" ? т.е. в рамках блока EnterCriticalSection & LeaveeCriticalSection ? или как?
да. именно так. причем лишний код лучше не закрывать критической секцией
HAS>и как лучше отсигналить?
Для сигнала, можно использовать Event. Чтобы основной поток постоянно не бегал поочереди, нужно сделать так: создается event, а в основном потоке цикл, бегающий по очереди, бегает не постоянно, а ждет сигнала с помощью WaitForSingleObject. Ждет на этом event. Когда event просигналит, значит в очереди что-то изменилось и теперь можно пройтись по ней. С другой стороны поток который скачивает страницу, меняет ее статус (синхронно) и устанавливает event, следовательно просыпается основной поток и находит в очереди эту страницу.
HAS>в принципе так все и сделано: HAS>1. есть update, который бегает по очереди и в зависимости от статуса страницы либо разбирает её, либо запускает новый поток HAS>2. есть OnFinishthread, который вызывается по завершению любого потока, скачавшего страницу. он меняет статус страницы на скачанный (в рамках EnterCS & LeaveSC) и вызывает update HAS>3. есть pushpage, который просто добавляет в конец очереди новую страницу HAS>4. есть parsepage, который вызывается только из update. Он выдирает ссылки и для каждой ссылки вызывает pushpage
HAS>где ошибка в логике/архитектуре?
На первый взгляд ошибок в логике и архитектуре нет, просто здесь каждый советует, как можно улучшить и сделать более ясной архитектуру, чтобы бесконечно не пришлось ловить ошибки. А вот встречный вопрос, а почему вы считаете, что раз не работает, значит ошибка именно в архитектуре? скорее всего банальный access violation на этапе parsepage.
HAS>хм... скажем, если все процедуры обернуть в EnterCS & LeaveCS, то доступ должен быть синхронизирован, правильно? — пусть даже в ущерб многопоточности... Пробовал и так, все по прежнему
если правильно обернуть, то да, должно работать. но оборачивая все подряд и на всякий случай, можно нарваться на другие ошибки. тот же deadlock. Да и если делать в ущерб многопоточности, то зачем она тогда нужна? А если только для проверки работает ли алгоритм, то вот вам и ответ на ваш вопрос(раз все по-прежнему): не работает, причем проблемы не из-за синхронизации.
Есть небольшой совет. Попробуйте все сделать для начала в одном потоке, без синхронизации. Мне кажется в рамках поставленной задачи можно обойтись вообще практически без синхронизации, ну или во всяком случае минимально. Каждый поток работает со своей страницей, закончил качать, запускай функцию parse, другой поток не лезет в эту страницу, зачем синхронизировать? Другое дело если есть еще какая то централизованная постобработка.
Re[6]: Многопоточность + синхронизация + как бороться?
Здравствуйте, Alexey Frolov, Вы писали:
AF>Здравствуйте, HAS, Вы писали:
AF>>>А кто сказал, что Thread — разделяемый ресурс? Во всяком случае нужно смотреть реализацию из которой это будет видно. А вообще судя по описаной логике, разделяемый ресурс — это очередь куда складываются и откуда достаются готовые, скачанные страницы. Вот обращения к ней push и pop нужно синхронизировать. В остальном синхронизация не нужна, пусть себе работают потоки (каждый со своим ресурсом) и сигналят о готовности.
HAS>>так и есть... при этом как такого pop'а нету, есть Update, который пробегает по всей очереди и в зависимости от статуса страницы либо вызывает ParsePage, либо PushPage, либо создает новый поток
AF>В данном конкретном случае Update проверяет статус страницы, значит кто-то его может менять, следовательно и проверять и обновлять этот статус нужно синхронно, т.е обеспечить невозможность одновременного доступа разных потоков к переменной отображающей статус страницы. Делать это можно либо оборачивая соответствующие участки кода в enter-leave critical section, либо с помощью interlocked... функций.
HAS>>что значит "синхронно закидывает страницу в очередь" ? т.е. в рамках блока EnterCriticalSection & LeaveeCriticalSection ? или как? AF>да. именно так. причем лишний код лучше не закрывать критической секцией
HAS>>и как лучше отсигналить?
AF>Для сигнала, можно использовать Event. Чтобы основной поток постоянно не бегал поочереди, нужно сделать так: создается event, а в основном потоке цикл, бегающий по очереди, бегает не постоянно, а ждет сигнала с помощью WaitForSingleObject. Ждет на этом event. Когда event просигналит, значит в очереди что-то изменилось и теперь можно пройтись по ней. С другой стороны поток который скачивает страницу, меняет ее статус (синхронно) и устанавливает event, следовательно просыпается основной поток и находит в очереди эту страницу.
тогда получается надо делать WaitForMultipleObjects, один Event для потока, другой для глобальной остановки, чтобы всегда можно было выйти из цикла в случае глюков, установив событие StopEvent (второе событие)... пральна?
п.с. подумаю как это сделать...
AF>если правильно обернуть, то да, должно работать. но оборачивая все подряд и на всякий случай, можно нарваться на другие ошибки. тот же deadlock. Да и если делать в ущерб многопоточности, то зачем она тогда нужна? А если только для проверки работает ли алгоритм, то вот вам и ответ на ваш вопрос(раз все по-прежнему): не работает, причем проблемы не из-за синхронизации.
ок, попробовал обернуть все в try catch и в каждом catch воткнул блок OutputDebugString(...) ; DebugBreak(); так вот 60% случаев (может и больше, но не меньше) прога вылетает не в catch, а х.з. где... как тут найти ошибку?
AF>Есть небольшой совет. Попробуйте все сделать для начала в одном потоке, без синхронизации. Мне кажется в рамках поставленной задачи можно обойтись вообще практически без синхронизации, ну или во всяком случае минимально. Каждый поток работает со своей страницей, закончил качать, запускай функцию parse, другой поток не лезет в эту страницу, зачем синхронизировать? Другое дело если есть еще какая то централизованная постобработка.
тут сложность вот какая: пока мы не скачаем первую страницу, не получим ссылки на другие и так далее для каждой полученной ссылки... качать страницы из инета многопоточно мне кажется быстрее (т.к. часть страниц могут просто по таймауту так ничего и не скачать, а другие в это время будут спокойно себе качать), соотв. эту цель и преследуем
Здравствуйте, HAS, Вы писали:
HAS>тогда получается надо делать WaitForMultipleObjects, один Event для потока, другой для глобальной остановки, чтобы всегда можно было выйти из цикла в случае глюков, установив событие StopEvent (второе событие)... пральна? HAS>п.с. подумаю как это сделать...
Да. Правильно. Но чтобы особо не мудрить могу предложить еще вариант. Event может сигнализировать не только об успешно скачанной странице, но и о необходимости завершить поток. Устанавливаем bTerminateNeeded=true и сигналим event, в цикле же первым делом проверяем эту переменную, а потом уже можно сканировать очередь.
HAS>ок, попробовал обернуть все в try catch и в каждом catch воткнул блок OutputDebugString(...) ; DebugBreak(); так вот 60% случаев (может и больше, но не меньше) прога вылетает не в catch, а х.з. где... как тут найти ошибку?
Посмотрел я мельком на код. Жестоко скажу я вам, вряд ли у кого то хватит терпения разобраться. Да еще и с таким форматированием. Может модераторы помогут и запихнут ваш код в соотв тэги.
Оберните в try catch основной код программы, может что-то все таки пропустили. Хотя сильно это не поможет, ну словите вы это exception и все... Но судя по тому что у вас много отладочных сообщений, вы должны хотя бы знать место где происходит обвал, озвучте его
HAS>тут сложность вот какая: пока мы не скачаем первую страницу, не получим ссылки на другие и так далее для каждой полученной ссылки... качать страницы из инета многопоточно мне кажется быстрее (т.к. часть страниц могут просто по таймауту так ничего и не скачать, а другие в это время будут спокойно себе качать), соотв. эту цель и преследуем
тем более этот код можно сделать однопоточным, оттестировать основные функции и переделать на многопоточный вариант.
Re[7]: Многопоточность + синхронизация + как бороться?
Hello HAS, you wrote:
> ок, попробовал обернуть все в try catch и в каждом catch воткнул блок OutputDebugString(...) ; DebugBreak(); так вот 60%
случаев (может и больше, но не меньше) прога вылетает не в catch, а х.з. где... как тут найти ошибку?
Я бы для начала попробовал вещи наподобии CodeGuard Билдера.
--
Всего хорошего, Слава
ICQ: 197577902
Posted via RSDN NNTP Server 2.0
Re[8]: Многопоточность + синхронизация + как бороться?
Здравствуйте, Slava Antonov, Вы писали:
>> ок, попробовал обернуть все в try catch и в каждом catch воткнул блок OutputDebugString(...) ; DebugBreak(); так вот 60% SA>случаев (может и больше, но не меньше) прога вылетает не в catch, а х.з. где... как тут найти ошибку?
выкинуть все try — catch нафиг и ловить exception.
как появился -- цепляешься отладчиком и отлаживаешь.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Signed, [TSS] /SDL/
Re[3]: Многопоточность + синхронизация + как бороться?
Здравствуйте, HAS, Вы писали:
HAS>количество потоков я ограничиваю некой величиной MAXTHREAD ( <= 10), т.е. если в работе MAXTHREAD потоков, то другие мы не запускаем
Все равно, запуск потока — дорогая операция.
VB>>Лучшим будет решение с пулом потоков, каждый из которых в цикле выбирает адреса страниц из глобального списка, скачивает страницу и анализирует результат. Соответственно, синхронизация нужна только при доступе к этому списку, ну и при помещении результата куда надо, конечно. Количество потоков легко регулировать в соответствии с загрузкой системой и количеством процессоров. Вариант — два пула потоков, один скачивает страницы, другой обрабатывает. Примерно так работают веб-сервера.
HAS>не совсем понял что значит два пула потоков... как это выглядит (архитектурно — структурно)
Есть два списка:
1. список адресов, которые надо скачать
2. список скачанных страниц, которые надо обработать
Есть две функции:
1. первая в бесконечном цикле выбирает очередной элемент из первого списка (ждет, если ничего нет), скачивает страницу, помещает результат во второй список
2. вторая в бесконечном цикле выбирает очередной элемент из второго списка (ждет, если ничего нет) и обрабатывает страницу.
Запускается N потоков с функцией 1 в качестве потоковой функции, и M потоков с функцией 2. N и M можно регулировать независимо (первые в основном расходуют сетевые ресурсы, вторые — процессорное время).
На самом деле, вместо первых можно использовать один поток, запуская операции скачивания асинхронно.
Re[8]: Многопоточность + синхронизация + как бороться?
Здравствуйте, Alexey Frolov, Вы писали:
AF>Здравствуйте, HAS, Вы писали:
HAS>>ок, попробовал обернуть все в try catch и в каждом catch воткнул блок OutputDebugString(...) ; DebugBreak(); так вот 60% случаев (может и больше, но не меньше) прога вылетает не в catch, а х.з. где... как тут найти ошибку?
AF>Посмотрел я мельком на код. Жестоко скажу я вам, вряд ли у кого то хватит терпения разобраться. Да еще и с таким форматированием. Может модераторы помогут и запихнут ваш код в соотв тэги.
согласен, тяжело... попытался обрамить код в тэг <pre>, не проканало
AF>Оберните в try catch основной код программы, может что-то все таки пропустили. Хотя сильно это не поможет, ну словите вы это exception и все... Но судя по тому что у вас много отладочных сообщений, вы должны хотя бы знать место где происходит обвал, озвучте его
все обернуто
HAS>>тут сложность вот какая: пока мы не скачаем первую страницу, не получим ссылки на другие и так далее для каждой полученной ссылки... качать страницы из инета многопоточно мне кажется быстрее (т.к. часть страниц могут просто по таймауту так ничего и не скачать, а другие в это время будут спокойно себе качать), соотв. эту цель и преследуем
AF>тем более этот код можно сделать однопоточным, оттестировать основные функции и переделать на многопоточный вариант.
пробовал...
Re[8]: Многопоточность + синхронизация + как бороться?
Здравствуйте, Slava Antonov, Вы писали:
SA>Hello HAS, you wrote:
>> ок, попробовал обернуть все в try catch и в каждом catch воткнул блок OutputDebugString(...) ; DebugBreak(); так вот 60% SA>случаев (может и больше, но не меньше) прога вылетает не в catch, а х.з. где... как тут найти ошибку?
SA>Я бы для начала попробовал вещи наподобии CodeGuard Билдера.
так он прикручен, все галочки выставлены... молчит зараза!
SA>-- SA>Всего хорошего, Слава SA>ICQ: 197577902
Re[9]: Многопоточность + синхронизация + как бороться?
Здравствуйте, TSS, Вы писали:
TSS>Здравствуйте, Slava Antonov, Вы писали:
>>> ок, попробовал обернуть все в try catch и в каждом catch воткнул блок OutputDebugString(...) ; DebugBreak(); так вот 60% SA>>случаев (может и больше, но не меньше) прога вылетает не в catch, а х.з. где... как тут найти ошибку?
TSS>выкинуть все try — catch нафиг и ловить exception. TSS>как появился -- цепляешься отладчиком и отлаживаешь.
Итак, проблема найдена — все дело в компоненте TIdHTTP (Indy 9.0), который начинает глючить когда происходит скачивание большого числа страниц ( > 1000). Ощущение что либо он что-то там не до закрывает и система начинает ругаться вследствие ограниченности ресурсов, либо в нем самом я что-то не так делаю ((
все это в потоке... попробовал на его место воткнуть TNMHTTP, та же история... когда же заменяю просто копированием файла (тестирую на локальном сервере, т.е. меняю http://localhost... на c:\\www\www...)? то все работает замечтательно! сайт из 6666 страниц в цикле скачивался 23 раза и за все время ни разу не было ни одного глюка...
Здравствуйте, HAS, Вы писали:
HAS>Итак, проблема найдена — все дело в компоненте TIdHTTP (Indy 9.0), который начинает глючить когда происходит скачивание большого числа страниц ( > 1000). Ощущение что либо он что-то там не до закрывает и система начинает ругаться вследствие ограниченности ресурсов, либо в нем самом я что-то не так делаю ((
HAS>вся работа с компонентом ограничена кодом:
HAS> TIdHTTP *http = new TIdHTTP(NULL); http->>Request->UserAgent = "Mozilla/3.0 (compatible"; http->>Request->AcceptLanguage = "es-us"; http->>ReadTimeout = 30000; http->>Get(Data); HAS> delete http;
HAS>все это в потоке...
Вопрос: можно ли с помощью этой компоненты скачивать множество страниц (по порядку, одну за другой), не создавая объект каждый раз заново? И будет ли при этом он по-прежнему глючить?
Re[10]: Многопоточность + синхронизация + как бороться?
Здравствуйте, HAS, Вы писали:
>>>> ок, попробовал обернуть все в try catch и в каждом catch воткнул блок OutputDebugString(...) ; DebugBreak(); так вот 60% SA>>>случаев (может и больше, но не меньше) прога вылетает не в catch, а х.з. где... как тут найти ошибку?
TSS>>выкинуть все try — catch нафиг и ловить exception. TSS>>как появился -- цепляешься отладчиком и отлаживаешь.
HAS>это как? в смысле как поймать без try catch?
Все подобные ошибки я обычно отлавливаю именно так. Т.е. убираю всю обработку исключений (кроме тех которые являются "запланированным", например _com_error-ы) и, при возникновении исколючений, цепляю отладчик и просматриваю стеки потоков.
Могу предположить, что у тебя производится работа с памятью, которая уже была освобождена, например автоматические переменные на стеке потока (а поток-то вышел) и т.п.
Здравствуйте, Vadim B, Вы писали:
VB>Здравствуйте, HAS, Вы писали:
HAS>>Итак, проблема найдена — все дело в компоненте TIdHTTP (Indy 9.0), который начинает глючить когда происходит скачивание большого числа страниц ( > 1000). Ощущение что либо он что-то там не до закрывает и система начинает ругаться вследствие ограниченности ресурсов, либо в нем самом я что-то не так делаю ((
HAS>>вся работа с компонентом ограничена кодом:
HAS>> TIdHTTP *http = new TIdHTTP(NULL); http->>>Request->UserAgent = "Mozilla/3.0 (compatible"; http->>>Request->AcceptLanguage = "es-us"; http->>>ReadTimeout = 30000; http->>>Get(Data); HAS>> delete http;
HAS>>все это в потоке...
VB>Вопрос: можно ли с помощью этой компоненты скачивать множество страниц (по порядку, одну за другой), не создавая объект каждый раз заново? И будет ли при этом он по-прежнему глючить?
даже если и можно, вариант одна за другой в одном потоке не годится... был опробован вариант работы нескольких потоков без их уничтожения (отработал, взял сл. страницу и т.д.), т.е. компонент создавался единожды для каждого потока — история та же самая...
Уроме того, попробовал заменить его компонентом TNMHTTP, те же самы е глюки... ощущение что в системе работает не совсем правильно... (XP SP2)
Здравствуйте, HAS, Вы писали:
HAS>Здравствуйте, Vadim B, Вы писали:
VB>>Здравствуйте, HAS, Вы писали:
HAS>>>Итак, проблема найдена — все дело в компоненте TIdHTTP (Indy 9.0), который начинает глючить когда происходит скачивание большого числа страниц ( > 1000). Ощущение что либо он что-то там не до закрывает и система начинает ругаться вследствие ограниченности ресурсов, либо в нем самом я что-то не так делаю ((
HAS>>>вся работа с компонентом ограничена кодом:
HAS>>>
Здравствуйте, aset, Вы писали:
A>Здравствуйте, HAS, Вы писали:
HAS>>Здравствуйте, Vadim B, Вы писали:
VB>>>Здравствуйте, HAS, Вы писали:
HAS>>>>Итак, проблема найдена — все дело в компоненте TIdHTTP (Indy 9.0), который начинает глючить когда происходит скачивание большого числа страниц ( > 1000). Ощущение что либо он что-то там не до закрывает и система начинает ругаться вследствие ограниченности ресурсов, либо в нем самом я что-то не так делаю ((
HAS>>>>вся работа с компонентом ограничена кодом:
HAS>>>> A>
A>TIdHTTP *http = new TIdHTTP(NULL);
http->>Request->UserAgent = "Mozilla/3.0 (compatible;)";
http->>Request->AcceptLanguage = "es-us";
http->>ReadTimeout = 30000;
http->>Get(Data);
A> delete http;
A>
A>Скорее всего могут исчерпаться хэндлы, GetLastError() не пробовали вызывать?
1. не пробовал
2. так по идее при убиении компонента все хэндлы должны закрываться?!
3. смотрел по ТаскМанагеру, число используемых хэндлов всегда в неких жестких пределах (т.е. то прибавится, то убавится, но всегда в каких-то жестких рамках)
Здравствуйте, HAS, Вы писали:
VB>>Вопрос: можно ли с помощью этой компоненты скачивать множество страниц (по порядку, одну за другой), не создавая объект каждый раз заново? И будет ли при этом он по-прежнему глючить?
HAS>даже если и можно, вариант одна за другой в одном потоке не годится...
Ну почему же не годится? Я выше по теме рассказал об архитектуре, которую я считаю правильной — в ней как раз каждый поток запускается один раз на все время работы приложения и в нем в цикле выполняется извлечение адресов из списка, скачивание страниц и запись их в другой список. Ну впрочем, мое дело — посоветовать, твое дело — отказаться .
Re[11]: Многопоточность + синхронизация + как бороться?
Hello TSS, you wrote:
> Все подобные ошибки я обычно отлавливаю именно так. Т.е. убираю всю обработку исключений (кроме тех которые являются "запланированным", например _com_error-ы) и, при возникновении исколючений, цепляю отладчик и просматриваю стеки потоков.
Не знаю как у вас, а скажем отладчик Delphi и Builder ловит все исключения, даже которые обрабатываются самой программой. Поэтому смысла убирать try/catch — не вижу.
--
Всего хорошего, Слава
ICQ: 197577902
Posted via RSDN NNTP Server 2.0
Re[12]: Многопоточность + синхронизация + как бороться?
Здравствуйте, Slava Antonov, Вы писали:
>> Все подобные ошибки я обычно отлавливаю именно так. Т.е. убираю всю обработку исключений (кроме тех которые являются "запланированным", например _com_error-ы) и, при возникновении исколючений, цепляю отладчик и просматриваю стеки потоков.
SA>Не знаю как у вас, а скажем отладчик Delphi и Builder ловит все исключения, даже которые обрабатываются самой программой. Поэтому смысла убирать try/catch — не вижу.
Простите за вопрос, а Вы еще какие-нибудь отладчики, кроме багландовских знаете?
Например OllyDbg, WinDbg, MS Visual Studio, SoftIce ?
В MSVS, например, есть возможность выставить исключения, которые должен ловить отладчик, если его не поймала программа.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Signed, [TSS] /SDL/
Re[13]: Многопоточность + синхронизация + как бороться?
Hello TSS, you wrote:
> Простите за вопрос, а Вы еще какие-нибудь отладчики, кроме багландовских знаете?
Знаю, но какое это отношение имеет к теме?
> В MSVS, например, есть возможность выставить исключения, которые должен ловить отладчик, если его не поймала программа.
И?
Пиписьками давайте в другом месте меряться.
ЗЫ: Отладчики Delphi и Builder тоже позволяют указывать какие исключения нам нужны, а какие нет.