Многопоточность + синхронизация + как бороться?
От: HAS Россия hasalex@mail.ru
Дата: 05.03.06 09:55
Оценка:
Уважаемые, встал в тупик и не знаю что делать, подскажите плз:

Есть задача: многопоточная обработка данных. В частности: необходимо многопоточно анализировать веб страницы. С этих страниц мы выдираем ссылки и их анализируем и т.д.

Как мне это видится:
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: Многопоточность + синхронизация + как бороться?
От: Slava Antonov Россия http://deadbeef.narod.ru
Дата: 05.03.06 10:42
Оценка:
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]: Многопоточность + синхронизация + как бороться?
От: HAS Россия hasalex@mail.ru
Дата: 05.03.06 12:56
Оценка:
Здравствуйте, 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]: Многопоточность + синхронизация + как бороться?
От: Slava Antonov Россия http://deadbeef.narod.ru
Дата: 05.03.06 13:30
Оценка:
Hello HAS, you wrote:

>> Данная проверка бессмысленна, т.к. EnterCriticalSection ГАРАНТИРУЕТ, что данный блок выполняется только одним потоком.

> тогда почему срабатывает данное исключение?

Какое исключение?

>> Что не работает?

> вроде как работает, но: один раз может отработать без задоринки, в другой раз может просто выдать "out of memory", в третий — подвиснуть... причем пытался уже статически задавать последовательность страниц, та же история...

Это нужно смотреть что у вас в ParsePage(Thread->Page);

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

> а как быть если мне необходимо, чтобы весь этот блок кода был выполнен без прерывания одним потоком, т.е. чтобы в этом блоке в ходе выполнения не было переключения между потоками?

Зачем? Какой вам тогда вообще смысл в многопоточности если вы ее накорню прибиваете.

--
Всего хорошего, Слава
ICQ: 197577902
Posted via RSDN NNTP Server 2.0
Re[4]: Многопоточность + синхронизация + как бороться?
От: HAS Россия hasalex@mail.ru
Дата: 05.03.06 13:35
Оценка:
Здравствуйте, 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: Многопоточность + синхронизация + как бороться?
От: Ligen Украина http://zone-of-ambiguity.blogspot.com/
Дата: 05.03.06 15:19
Оценка:
<skipped>

HAS>Подобный вариант НЕ РАБОТАЕТ ((


HAS>Как подобное лучше сделать?!


Ты ее инициализируешь? Покажи весь код
См. MSDN::InitializeCriticalSection — может выбрасывать exception

З.Ы. Оберни нативную CS врапперами, замучаешься везде писать try/catch
Viva el Junta Militar! Viva el Presidente!
Re[5]: Многопоточность + синхронизация + как бороться?
От: npo_mir  
Дата: 06.03.06 02:51
Оценка:
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: Многопоточность + синхронизация + как бороться?
От: remark Россия http://www.1024cores.net/
Дата: 06.03.06 03:10
Оценка:
Здравствуйте, HAS, Вы писали:

HAS>void OnFinishThread(Tthread *Thread)

HAS>{
HAS> EnterCriticalSection(&CS);
HAS> try
HAS> {
HAS> if (Locker > 0) throw Exception("Херня, залезло слишком много потоков одновременно ");
HAS> ++Locker;
HAS> try
HAS> {
HAS> ParsePage(Thread->Page);
HAS> }__finally
HAS> {
HAS> --Locker;
HAS> };
HAS> }__finally
HAS> {
HAS> LeaveCriticalSection(&CS);
HAS> };
HAS>};

HAS>Подобный вариант НЕ РАБОТАЕТ ((


HAS>Как подобное лучше сделать?!



Здесь вообще не нужна синхронизация, т.к. нет работы с разделяемыми ресурсами. Код можно переписать просто как:

void OnFinishThread(Tthread *Thread)
{
  ParsePage(Thread->Page); 
}


Пока поток работает только с локальными данными и не обращается ни к каким глобальным данным (к которым одновременно обращаются сразу несколько потоков) никакая синхронизация не нужна



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: Многопоточность + синхронизация + как бороться?
От: npo_mir  
Дата: 06.03.06 05:32
Оценка:
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]: Многопоточность + синхронизация + как бороться?
От: Alexey Frolov Беларусь  
Дата: 06.03.06 10:35
Оценка:
Здравствуйте, 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: Многопоточность + синхронизация + как бороться?
От: kalikanziris  
Дата: 06.03.06 23:19
Оценка:

Здравствуйте, HAS, Вы писали:

HAS>Уважаемые, встал в тупик и не знаю что делать, подскажите плз:

HAS>Вопрос: с точки зрения многопоточности критичная для нас функция OnFinishThread, т.к. ее могут вызвать сразу несколько потоков одновременно, прально? Соотв. делаем, например, так:

HAS>void OnFinishThread(Tthread *Thread)
HAS>{
HAS> EnterCriticalSection(&CS);
HAS> try
HAS> {
HAS> if (Locker > 0) throw Exception("Херня, залезло слишком много потоков одновременно ");
HAS> ++Locker;
HAS> try
HAS> {
HAS> ParsePage(Thread->Page);
HAS> }__finally
HAS> {
HAS> --Locker;
HAS> };
HAS> }__finally
HAS> {
HAS> LeaveCriticalSection(&CS);
HAS> };
HAS>};

HAS>Подобный вариант НЕ РАБОТАЕТ ((

HAS>Как подобное лучше сделать?!


Ведь при входе в OnFinishThread можно выставить семафор, а перед вызовом OnFinishThread его проверять и оценивать необходимость запуска.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[6]: Многопоточность + синхронизация + как бороться?
От: HAS Россия hasalex@mail.ru
Дата: 08.03.06 07:09
Оценка:
Здравствуйте, 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.


_>В любом случае это нужно все прогнать под отладчиком.


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

>>> Зачем? Какой вам тогда вообще смысл в многопоточности если вы ее накорню прибиваете.

>> почему?

_>Не, если функция состоит всего из одной простой операции, то все нормально. Но если там что-нить посложнее, то получится что остальные потоки вместо того чтобы делать свое дело будут тупо ждать пока функция завершит свою работу, что не есть хорошо.


поток только качает страницу и скидывает её в файлик

_>Как я уже говорил, синхронизировать доступ нужно не к функциям, а к разделяемым ресурсам. В вашем случае — это та очередь с которой вы работаете.


пример работы с этой очередью:

void __fastcall THttpDownloadManager::Update()
{
EnterCriticalSection(&CS);
try
{
#define MaxThreadCount 20
while ((FAddressWaiting > 0 || FAddressDownloaded > 0) && FAddressDownloading < MaxThreadCount && !FPause && !FTerminate)
{
TAddressInfo *Info = NULL;
for (int i=FAddresses->Count-1; i>=0 && FAddressDownloading < MaxThreadCount; --i)
{
Info = (TAddressInfo*)FAddresses->Items[i];
if (Info == NULL) continue;
switch (Info->State)
{
case AS_INORDER: {
if ((FAddressWaiting > 0) && (FAddressDownloading < MaxThreadCount)) DownloadPage(Info);
break;
};
case AS_ERROR: {
++FAddressError;
--FAddressDownloaded;
++FAddressReady;
Info->State = AS_IDLE;
DoAddressStateChange(Info);
break;
};
case AS_DOWNLOADED: {
--FAddressDownloaded;
++FAddressReady;
if (Info->Recursive)
ParsePage(Info);
Info->State = AS_READY;
DoAddressStateChange(Info);
break;
};
};
};
};
if (FAddressReady == FAddressCount | FTerminate)
{
FState = (FTerminate) ? DM_TERMINATE: DM_READY;
if (FOnFinished != NULL) FOnFinished(NULL);
};

}__finally
{
LeaveCriticalSection(&CS);
};
};

---
при этом DownloadPage создает и запускает поток, ParsePage — выдирает из страницы ссылки и добавляет их в очередь через PushPage (pushpage заключен между EnterCriticalSection & LeaveCriticalSection). Чтоздесь не так?

_>--

_>Всего хорошего, Слава.
_>ICQ: 197577902
Re[2]: Многопоточность + синхронизация + как бороться?
От: HAS Россия hasalex@mail.ru
Дата: 08.03.06 07:14
Оценка:
Здравствуйте, Ligen, Вы писали:

L><skipped>


HAS>>Подобный вариант НЕ РАБОТАЕТ ((


HAS>>Как подобное лучше сделать?!


L>Ты ее инициализируешь? Покажи весь код


инициализация происходит в конструкторе менеджера, а убиение соотв. в деструкторе...

L>См. MSDN::InitializeCriticalSection — может выбрасывать exception


ну тогда наверное и менеджер не стал бы работать? а он работает... вылетает просто от балды (

L>З.Ы. Оберни нативную CS врапперами, замучаешься везде писать try/catch


типа чтобы юзать так:

void ....
{
TCSWrapper Wrapper(&CS); // в конструкторе EnterCriticalSection
...
// а здесь типа срабатывает деструктор с LeaveCriticalSection
};

??? Есть гарантия 100% что деструктор всегда будет отрабатывать во время и верно?
Re[2]: Многопоточность + синхронизация + как бороться?
От: HAS Россия hasalex@mail.ru
Дата: 08.03.06 07:16
Оценка:
Здравствуйте, remark, Вы писали:

R>Здравствуйте, HAS, Вы писали:


HAS>>void OnFinishThread(Tthread *Thread)

HAS>>{
HAS>> EnterCriticalSection(&CS);
HAS>> try
HAS>> {
HAS>> if (Locker > 0) throw Exception("Херня, залезло слишком много потоков одновременно ");
HAS>> ++Locker;
HAS>> try
HAS>> {
HAS>> ParsePage(Thread->Page);
HAS>> }__finally
HAS>> {
HAS>> --Locker;
HAS>> };
HAS>> }__finally
HAS>> {
HAS>> LeaveCriticalSection(&CS);
HAS>> };
HAS>>};

HAS>>Подобный вариант НЕ РАБОТАЕТ ((


HAS>>Как подобное лучше сделать?!



R>Здесь вообще не нужна синхронизация, т.к. нет работы с разделяемыми ресурсами. Код можно переписать просто как:


R>
R>void OnFinishThread(Tthread *Thread)
R>{
R>  ParsePage(Thread->Page); 
R>}
R>


R>Пока поток работает только с локальными данными и не обращается ни к каким глобальным данным (к которым одновременно обращаются сразу несколько потоков) никакая синхронизация не нужна


так Thread->Page и есть глобальные данные, с которыми происходит работа в нескольких местах программы

R>
Re[4]: Многопоточность + синхронизация + как бороться?
От: HAS Россия hasalex@mail.ru
Дата: 08.03.06 07:24
Оценка:
Здравствуйте, 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 Россия hasalex@mail.ru
Дата: 08.03.06 07:30
Оценка:
Здравствуйте, kalikanziris, Вы писали:

K>

K>Здравствуйте, HAS, Вы писали:

HAS>>Уважаемые, встал в тупик и не знаю что делать, подскажите плз:

HAS>>Вопрос: с точки зрения многопоточности критичная для нас функция OnFinishThread, т.к. ее могут вызвать сразу несколько потоков одновременно, прально? Соотв. делаем, например, так:

HAS>>void OnFinishThread(Tthread *Thread)
HAS>>{
HAS>> EnterCriticalSection(&CS);
HAS>> try
HAS>> {
HAS>> if (Locker > 0) throw Exception("Херня, залезло слишком много потоков одновременно ");
HAS>> ++Locker;
HAS>> try
HAS>> {
HAS>> ParsePage(Thread->Page);
HAS>> }__finally
HAS>> {
HAS>> --Locker;
HAS>> };
HAS>> }__finally
HAS>> {
HAS>> LeaveCriticalSection(&CS);
HAS>> };
HAS>>};

HAS>>Подобный вариант НЕ РАБОТАЕТ ((

HAS>>Как подобное лучше сделать?!


K>Ведь при входе в OnFinishThread можно выставить семафор, а перед вызовом OnFinishThread его проверять и оценивать необходимость запуска.


да, можно. В принципе можно использовать любую методику синхронизации... Мне важно другое: с точки зрения синхронизации здесь вроде все правильно. Соотв. мне ВАЖНО ПОНЯТЬ, где что и почему не работает, дабы не наступать на эти же грабли в будущем!
Re[5]: Многопоточность + синхронизация + как бороться?
От: Slava Antonov Россия http://deadbeef.narod.ru
Дата: 08.03.06 15:31
Оценка:
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]: Многопоточность + синхронизация + как бороться?
От: Slava Antonov Россия http://deadbeef.narod.ru
Дата: 08.03.06 15:31
Оценка:
Hello HAS, you wrote:

> заманался гонять, такое ощущение, что отладчик глюкаво работает


Маловероятно.

> потому как очень часто выкидывает на код, где в принципе ничего быть не должно (там даже многопоточности нету)


Видимо в каком-то месте произошла порча памяти и что-нить подобное. Какая ошибка при этом возникает? AV?

--
Всего хорошего, Слава
ICQ: 197577902
Posted via RSDN NNTP Server 2.0
Re: Многопоточность + синхронизация + как бороться?
От: Vadim B  
Дата: 08.03.06 19:23
Оценка:
Здравствуйте, 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]: Многопоточность + синхронизация + как бороться?
От: HAS Россия hasalex@mail.ru
Дата: 09.03.06 09:38
Оценка:
Здравствуйте, Slava Antonov, Вы писали:

SA>Hello HAS, you wrote:


>> заманался гонять, такое ощущение, что отладчик глюкаво работает


SA>Маловероятно.


>> потому как очень часто выкидывает на код, где в принципе ничего быть не должно (там даже многопоточности нету)


SA>Видимо в каком-то месте произошла порча памяти и что-нить подобное. Какая ошибка при этом возникает? AV?


либо Исключение, либо просто выбрасывает в окно CPU Debug...
п.с. а что такое AV ?

SA>--

SA>Всего хорошего, Слава
SA>ICQ: 197577902
Re[6]: Многопоточность + синхронизация + как бороться?
От: HAS Россия hasalex@mail.ru
Дата: 09.03.06 09:40
Оценка:
Здравствуйте, 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]: Многопоточность + синхронизация + как бороться?
От: HAS Россия hasalex@mail.ru
Дата: 09.03.06 09:43
Оценка:
Здравствуйте, 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]: Многопоточность + синхронизация + как бороться?
От: npo_mir  
Дата: 09.03.06 10:29
Оценка:
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]: Многопоточность + синхронизация + как бороться?
От: npo_mir  
Дата: 09.03.06 10:33
Оценка:
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]: Многопоточность + синхронизация + как бороться?
От: Alexey Frolov Беларусь  
Дата: 09.03.06 14:14
Оценка:
Здравствуйте, 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]: Многопоточность + синхронизация + как бороться?
От: HAS Россия hasalex@mail.ru
Дата: 09.03.06 15:36
Оценка:
Здравствуйте, 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, другой поток не лезет в эту страницу, зачем синхронизировать? Другое дело если есть еще какая то централизованная постобработка.


тут сложность вот какая: пока мы не скачаем первую страницу, не получим ссылки на другие и так далее для каждой полученной ссылки... качать страницы из инета многопоточно мне кажется быстрее (т.к. часть страниц могут просто по таймауту так ничего и не скачать, а другие в это время будут спокойно себе качать), соотв. эту цель и преследуем
Re[7]: код...
От: HAS Россия hasalex@mail.ru
Дата: 09.03.06 16:00
Оценка:
Пожалуйста, не сочтите за наглость, но приведу свой код. Ткните меня плз в те места, где по вашему мнению "так делать нельзя!"...

// модуль downloadmanager.cpp

<pre>
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop
#pragma package(smart_init)

#include "download_manager.h"

#ifdef _DEBUG
#define StartProc(val) //OutputDebugString(("enter to: "+AnsiString(__FUNC__)+val).c_str())
#define EndProc(val) //OutputDebugString(("exit from: "+AnsiString(__FUNC__)+val).c_str())
#define OutputDebugString(val) //
#else
#define StartProc(val)
#define EndProc(val)
#define OutputDebugString(val) //
#endif
const AnsiString Pages[] = {
"http://localhost/index.html",
"http://localhost/page1.html",
"http://localhost/page2.html",
"http://localhost/page3.html",
"http://localhost/page4.html",
"http://localhost/page5.html",
"http://localhost/tasm/i1.html",
"http://localhost/tasm/i2.html",
"http://localhost/tasm/0.html",
"http://localhost/tasm/1.html",
"http://localhost/tasm/2.html",
"http://localhost/tasm/3.html",
"http://localhost/tasm/4.html",
"http://localhost/tasm/5.html",
"http://localhost/tasm/6.html",
"http://localhost/tasm/7.html",
"http://localhost/tasm/8.html",
"http://localhost/tasm/9.html",
"http://localhost/tasm/10.html",
"http://localhost/tasm/11.html",
"http://localhost/tasm/12.html",
"http://localhost/tasm/13.html",
"http://localhost/tasm/14.html",
"http://localhost/tasm/15.html",
"http://localhost/tasm/16.html",
"http://localhost/tasm/17.html",
"http://localhost/tasm/18.html",
"http://localhost/tasm/19.html",
"http://localhost/tasm/20.html",
"http://localhost/tasm/21.html",
"http://localhost/tasm/22.html",
"http://localhost/tasm/23.html",
"http://localhost/tasm/24.html",
"http://localhost/tasm/25.html",
"http://localhost/tasm/26.html",
"http://localhost/tasm/27.html",
"http://localhost/tasm/28.html",
"http://localhost/tasm/29.html",
"http://localhost/tasm/30.html",
"http://localhost/tasm/31.html",
"http://localhost/tasm/32.html",
"http://localhost/tasm/33.html",
"http://localhost/tasm/34.html",
"http://localhost/tasm/35.html",
"http://localhost/tasm/36.html",
"http://localhost/tasm/37.html",
"http://localhost/tasm/38.html",
"http://localhost/tasm/39.html",
"http://localhost/tasm/40.html",
"http://localhost/tasm/41.html",
"http://localhost/tasm/42.html",
"http://localhost/tasm/43.html",
"http://localhost/tasm/44.html",
"http://localhost/tasm/45.html"

};
int Count = 10;
//---------------------------------------------------------------------------
__fastcall THttpDownloadManager::THttpDownloadManager(const AnsiString &PathForSave)
{
StartProc("");
InitializeCriticalSection(&CS);

FOnFinished = NULL;

FTerminate = FPause = false;
FAddressCount = FAddressWaiting = FAddressDownloading =
FAddressReady = FAddressError = FInAnalyse = FAddressDownloaded = 0;
FState = DM_BUSY;
Tag = 0;

if (PathForSave[PathForSave.Length()] != '\\' &&
PathForSave[PathForSave.Length()] != '/') FTempDirectory = PathForSave + "/";
else FTempDirectory = PathForSave;

FAddresses = new TThreadList();
FOnAddressStateChange = NULL;

FCacheAddress = new TStringList();
FCacheAddress->Sorted = true;
FCacheAddress->Duplicates = dupError;

Document = new THTMLDocument();
EndProc("");
};

__fastcall THttpDownloadManager::~THttpDownloadManager()
{
StartProc("");

DeleteCriticalSection(&CS);

TList *FAddressesData = FAddresses->LockList();
for (int i=FAddressesData->Count-1; i>=0; i--)
{
TAddressInfo *Info = (TAddressInfo*)FAddressesData->Items[i];
if (Info != NULL)
{
if (Info->Address != NULL) { delete Info->Address; Info->Address = NULL; };
delete Info; Info = NULL;
};
FAddressesData->Delete(i);
};
FAddresses->UnlockList();

delete FAddresses; FAddresses = NULL;
delete FCacheAddress; FCacheAddress = NULL;
delete Document; Document = NULL;
EndProc("");
};

void __fastcall THttpDownloadManager::Terminate()
{
if (!FTerminate)
{
FTerminate = true;
Update();
};
};

void __fastcall THttpDownloadManager::Pause()
{
if (!FTerminate && !FPause) FPause = true;
};

void __fastcall THttpDownloadManager::Resume()
{
if (!FTerminate && FPause)
{
FPause = false;
Update();
};
};

void __fastcall THttpDownloadManager::OnFinishDownloadThread(TObject *Sender)
{
EnterCriticalSection(&CS);
try
{
T_HAS_Adapter *Thread = (T_HAS_Adapter*)Sender;
if (Thread != NULL && Thread->State)
{
--FAddressDownloading;
++FAddressDownloaded;
Thread->Info->State = Thread->TempState;
Thread->Info->Thread = NULL;
}
else OutputDebugString("error thread: out of memory");
}__finally
{
Update();
LeaveCriticalSection(&CS);
};
return;

// EnterCriticalSection(&CS);

/* StartProc("");
try
{try{
if (Tag > 0)
{
DebugBreak();
return;
// throw Exception("blya!");
// Application->Terminate();
};
InterlockedIncrement(&Tag);
T_HAS_Adapter *Thread = dynamic_cast<T_HAS_Adapter*>(Sender);
if (Thread != NULL)
{
try
{
int RetValue = Thread->RetValue;
TAddressInfo *Info = Thread->Info;
Info->Thread = NULL;

--FAddressDownloading;
++FAddressReady;
if (RetValue != HA_RV_OK) ++FAddressError;

if (FAddresses->IndexOf(Info) != -1)
{
if (RetValue == HA_RV_OK)
{
try
{
Info->State = AS_READY;
if (Info->Recursive)
try{
ParsePage(Info);
}catch(...)
{
DebugBreak();
OutputDebugString("Error in 3_OnFinishThread:: ParsePage");
OutputDebugString(Info->Address->Address.c_str());
// Application->Terminate();
};
DoAddressStateChange(Info);
}catch(...)
{
DebugBreak();
OutputDebugString("Error in 2_OnFinishThread");
// Application->Terminate();
};
}
else
{
try{
Info->State = AS_ERROR;
DoAddressStateChange(Info);
}catch(...)
{
DebugBreak();
OutputDebugString("Error in 1_OnFinishThread");
// Application->Terminate();
};

};
};
}catch(...)
{
DebugBreak();
OutputDebugString("Error in _OnFinishThread");
// Application->Terminate();
};

};
}catch(...)
{
DebugBreak();
OutputDebugString("Error in OnFinishThread!");
// Application->Terminate();
};
}__finally
{
InterlockedDecrement(&Tag);
Update();

EndProc("");

// LeaveCriticalSection(&CS);
};
*/
};

void __fastcall THttpDownloadManager::StartByDomain(const AnsiString &HttpAddress)
// íà÷èíàåò ðåêóðñèâíîå ñêà÷èâàíèå ñòðàíèö ñ ãëàâíîãî àäðåñà
{
StartProc("");

FState = DM_DOWNLOAD;
AnsiString Address = HttpAddress;
if (CorrectAddress(Address)) PushAddress(Address);
// else throw EDownloadManager(DM_ERROR_ADDRESS);
Update();

EndProc("");
};

void __fastcall THttpDownloadManager::PushAddress(const THttpAddress &Address, TAddressInfo *Parent, bool Recursive)
{
EnterCriticalSection(&CS);
try
{
try
{
StartProc("");
if (!Address.IsCreated) return;

bool Added = false;

FState = DM_DOWNLOAD;
AnsiString LocalFileName = CreateLocalAddress(Address.Address);
AnsiString LocalDir = ExtractFilePath(LocalFileName);
if (LocalDir == "" || ForceDirectories(LocalDir)) // íåëüçÿ âûçûâàòü ForxeDirectories ñ ïóñòîé ñòðîêîé
{
try
{
TAddressInfo *Info = new TAddressInfo();
try
{
Info->Address = new THttpAddress(Address);
try
{
FCacheAddress->Add(Address.Address); // may be catch if address alerady exists
Added = true;

Info->State = AS_INORDER;

Info->LocalFileName = LocalFileName;
Info->Thread = NULL;
if (Parent == NULL)
{
Info->Recursive = Recursive;
Info->Parent = NULL;
}
else
{
Info->Recursive = Parent->Recursive;
Info->Parent = Parent;
};

FAddresses->Add(Info);
DoAddressStateChange(Info);

++FAddressWaiting;
++FAddressCount;
}catch(...)
{
delete Info->Address; Info->Address = NULL;
delete Info; Info = NULL;
OutputDebugString(("Push: url: "+Address.Address+" already exist!").c_str());
};
}catch(...)
{
delete Info; Info = NULL;
throw Exception("Out of Memory httpaddress");
};
}catch(...)
{
throw Exception("Out of Memory addressinfo!");
};
}
else
{
OutputDebugString(("ForceDirectories error! Folder not create.\r\nDirectory: "+LocalDir).c_str());
DebugBreak();
};
}catch(...)
{
OutputDebugString("Error in putshpage");
DebugBreak();
};

}__finally
{
EndProc("");
LeaveCriticalSection(&CS);
};

};

void __fastcall THttpDownloadManager::Update()
{
EnterCriticalSection(&CS);
StartProc("");
try
{
#define MaxThreadCount 3
while ((FAddressWaiting > 0 || FAddressDownloaded > 0) && FAddressDownloading < MaxThreadCount && !FPause && !FTerminate)
{
TAddressInfo *Info = NULL;
TList *FAddressesData = FAddresses->LockList();
for (int i=FAddressesData->Count-1; i>=0 && FAddressDownloading < MaxThreadCount; --i)
{
Info = (TAddressInfo*)FAddressesData->Items[i];
if (Info == NULL) continue;
switch (Info->State)
{
case AS_INORDER: {
if ((FAddressWaiting > 0) /*&& (FAddressDownloading < MaxThreadCount)*/) DownloadPage(Info);
break;
};
case AS_ERROR: {
++FAddressError;
--FAddressDownloaded;
++FAddressReady;
Info->State = AS_IDLE;
DoAddressStateChange(Info);
break;
};
case AS_DOWNLOADED: {
--FAddressDownloaded;
++FAddressReady;
if (Info->Recursive)
ParsePage(Info);
Info->State = AS_READY;
DoAddressStateChange(Info);
break;
};
};
};
FAddresses->UnlockList();

};
if (FAddressReady == FAddressCount | FTerminate)
{
FState = (FTerminate) ? DM_TERMINATE: DM_READY;
if (FOnFinished != NULL) FOnFinished(NULL);
};

}__finally
{
EndProc("");
LeaveCriticalSection(&CS);
};
};

void __fastcall THttpDownloadManager::DownloadPage(TAddressInfo *Info)
{
EnterCriticalSection(&CS);
try
{
T_HAS_Adapter *Thread = NULL;
try
{
Thread = new T_HAS_Adapter(true, Info);
if (Thread->State == 0)
{
Thread->OnTerminate = OnFinishDownloadThread;
Thread->Info = Info;
Info->Thread = Thread;
Info->State = AS_DOWNLOADING;
Thread->Resume();

--FAddressWaiting;
++FAddressDownloading;
DoAddressStateChange(Info);
}
else throw Exception("Error create in thread");
}catch(Exception &E)
{
Info->Thread = NULL;
Info->State = AS_IDLE;
throw Exception(E.Message);
};
}__finally
{
LeaveCriticalSection(&CS);
};
};

bool __fastcall THttpDownloadManager::CorrectAddress(AnsiString &Address)
// êîððåêòèðóåò àäðåñ
{
StartProc("");

Address = AnsiLowerCase(StringReplace(Address, "\\", "/", TReplaceFlags() << rfReplaceAll));
if (Address.SubString(1, 7) != "http://") Address = "http://"+Address;

EndProc("");
return true;
};

AnsiString __fastcall THttpDownloadManager::CreateLocalAddress(const AnsiString &Address)
{
AnsiString LocalFileName = "";
StartProc("");
try
{
LocalFileName = Address;
if (LocalFileName.SubString(1, 7) == "http://") LocalFileName.Delete(1, 7);
LocalFileName = StringReplace(LocalFileName, ":", "_", TReplaceFlags() << rfReplaceAll);
LocalFileName = StringReplace(FTempDirectory + LocalFileName, "/", "\\", TReplaceFlags() << rfReplaceAll);
AnsiString Ext = ExtractFileExt(LocalFileName);
if (Ext == "") { LocalFileName += "index.html"; Ext = ".html"; };
if (int P = Ext.Pos("?"))
{
// Ext = Ext.SubString(P+1, Ext.Length())+Ext.SubString(1, P-1);
LocalFileName = ChangeFileExt(LocalFileName, Ext.SubString(P+1, Ext.Length())+Ext.SubString(1, P-1));
};
LocalFileName = StringReplace(LocalFileName, "?", "_", TReplaceFlags() << rfReplaceAll);
}catch(...)
{
OutputDebugString(("ERROR: CreateLocalAddress: "+Address).c_str());
DebugBreak();
LocalFileName = "";
}
return LocalFileName;
};

void __fastcall THttpDownloadManager::DoAddressStateChange(TAddressInfo *Info)
{
EnterCriticalSection(&CS);
if (FOnAddressStateChange != NULL) FOnAddressStateChange((void*)this, Info);
LeaveCriticalSection(&CS);
};

int __fastcall THttpDownloadManager::GetState()
{
return FState;
};

void __fastcall THttpDownloadManager::ParsePage(TAddressInfo *Info)
{
#define StaticPages
EnterCriticalSection(&CS);
try
{

Info->State = AS_ANALYZE;

DoAddressStateChange(Info);

#if defined(StaticPages)
++FInAnalyse;
for (int i=0; i<Count; i++)
{
PushAddress(THttpAddress(Info->Address->Address, Pages[i]), Info);
};

/* if (Count > 0)
{
Count--;
try
{
PushAddress(THttpAddress(Info->Address->Address, Pages[Count]), Info);
}catch(...)
{
DebugBreak();
OutputDebugString("ERROR: in ParsePage1");
OutputDebugString(Info->Address->Address.c_str());
};

AnsiString S = "";
try
{
try
{
S = Pages[random(40)];
}catch(...)
{
DebugBreak();
OutputDebugString("ERROR: in ParsePage3");
};
PushAddress(THttpAddress(Info->Address->Address, S), Info);
}catch(...)
{
DebugBreak();
OutputDebugString("ERROR: in ParsePage2");
};

};
*/
--FInAnalyse;
#else

try
{
if (Info == NULL || FInAnalyse > 0) return;
// if (Info->State != AS_DOWNLOADED) return;

++FInAnalyse;

Info->State = AS_ANALYZE;

DoAddressStateChange(Info);
try
{
// THTMLDocument *Document = new THTMLDocument();
// if (Document == NULL)
// {
// OutputDebugString("Out Of Memory!");
// Application->Terminate();
// }
// else
{
if (Document->LoadFromFile(Info->LocalFileName.c_str()))
{
IHTMLDocument2 *Doc = Document->Document;
if (Doc != NULL)
{
IHTMLElementCollection *All;
Doc->get_all(&All);

if (All != NULL)
{
wchar_t *TagsName[] = {L"a", L"frame"};
for (int TagsCount = 0; TagsCount < 2; TagsCount++)
{
IDispatch *pDisp = NULL;
VARIANT name;
name.vt = VT_BSTR;
name.bstrVal = TagsName[TagsCount];
All->tags(name, &pDisp);

if (pDisp != NULL)
{
IHTMLElementCollection *Tags;
pDisp->QueryInterface(IID_IHTMLElementCollection, (void**)&Tags);
if (Tags != NULL)
{
long Count = 0;
Tags->get_length(&Count);
for (int i=0; i<Count; i++)
{
IDispatch *disp;
Tags->item(OleVariant(i), OleVariant(0), &disp);
if (disp != NULL) //
{
IHTMLElement *Anchor;
disp->QueryInterface(IID_IHTMLElement, (void**)&Anchor);
if (Anchor != NULL)
{
wchar_t *tag;
Anchor->get_tagName(&tag);
AnsiString Tag = AnsiLowerCase(WideString(tag));
wchar_t *AttribName = NULL;
if (Tag == "a") AttribName = L"href";
else
if (Tag == "frame") AttribName = L"src";

if (AttribName != NULL)
{
VARIANT Attribute;
Anchor->getAttribute( AttribName, 2, &Attribute);
AnsiString href = VarToStr(Attribute);
int ddd = 0;
try
{
if (Attribute.vt == VT_BSTR)
{
THttpAddress Adr(Info->Address->Address, href);
ddd++;
try
{
PushAddress(Adr, Info);
}catch(...)
{
DebugBreak();
OutputDebugString("ERROR IN PARSE: PUSH");
// Application->Terminate();
};
ddd++;
};
}catch(...)
{
DebugBreak();
OutputDebugString("Error in parse");
// Application->Terminate();
};
}; // attribname != NULL
Anchor->Release();
}; //anchor != NULL
disp->Release();
}; //disp != NULL
}; // for i
Tags->Release();
}; // Tags != NULL
pDisp->Release();
}; // pDisp != NULL;
}; // for TagsCount
All->Release();
}; // All != NULL;
}; // Doc != NULL;
}; // LoadFromFile
// delete Document;
} // else
}catch(...)
{
DebugBreak();
OutputDebugString("Out of Memory!!!!");
// Application->Terminate();
};

}__finally
{
--FInAnalyse;
EndProc("");
// LeaveCriticalSection(&CS);
};
#endif
}__finally
{
LeaveCriticalSection(&CS);
};
};

</pre>





// его хидер

<pre>
//---------------------------------------------------------------------------

#ifndef download_managerH
#define download_managerH
//---------------------------------------------------------------------------
#include <sysutils.hpp>
#include <classes.hpp>
#include <filectrl.hpp>

#pragma hdrstop

#include "address.h"
#include "HTMLDoc.h"
#include "downloader_thread.h"

#define DM_ERROR_ADDRESS "error start address!"

typedef enum {DM_BUSY = 0, DM_DOWNLOAD, DM_READY, DM_TERMINATE, DM_COUNT_EVENTS } TDMState;

typedef Exception EDownloadManager;

typedef void __fastcall (__closure *TAddressEvent)(void* Sender, TAddressInfo *Info);
typedef void __fastcall (__closure *TFinishEvent)(void* Sender);

class THttpDownloadManager
{
private:
int FAddressCount; // âñåãî àäðåñîâ ïîëó÷åíî
int FAddressWaiting; // àäðåñîâ â îæèäàíèè ñêà÷èâàíèÿ
int FAddressDownloading; // â ïðîöåññå ñêà÷èâàíèÿ
int FAddressDownloaded; // ñêà÷àíî, íî íå ïðîàíàëèçèðîâàíî
int FAddressReady; // óæå ñêà÷àíî
int FInAnalyse;
int FAddressError; // ñêà÷àíî ñ îøèáêîé
TDMState FState;
long Tag;
// ïî èäåå FAddressCount = FAddressWaiting + FAddressDownloading + FAddressReady
CRITICAL_SECTION CS;
AnsiString FTempDirectory;
TThreadList *FAddresses;
TAddressEvent FOnAddressStateChange;
TStringList *FCacheAddress;
TFinishEvent FOnFinished;
bool FPause;
bool FTerminate;
THTMLDocument *Document;
void __fastcall OnFinishDownloadThread(TObject *Sender);
void __fastcall PushAddress(const THttpAddress &Address, TAddressInfo *Parent = NULL, bool Recursive = true);
bool __fastcall CorrectAddress(AnsiString &Address);
AnsiString __fastcall CreateLocalAddress(const AnsiString &Address);
void __fastcall DoAddressStateChange(TAddressInfo *Info);
void __fastcall ParsePage(TAddressInfo *Info);
public:
__fastcall THttpDownloadManager(const AnsiString &PathForSave);
__fastcall ~THttpDownloadManager();
void __fastcall Update();
void __fastcall StartByDomain(const AnsiString &HttpAddress);
void __fastcall DownloadPage(TAddressInfo *Info);
int __fastcall GetState();
void __fastcall Terminate();
void __fastcall Pause();
void __fastcall Resume();
__property int AddressCount = {read = FAddressCount};
__property int AddressWaiting = {read = FAddressWaiting};
__property int AddressDownloading = {read = FAddressDownloading };
__property int AddressReady = {read = FAddressReady };
__property int AddressError = {read = FAddressError };
__property int AddressAnalyze = {read = FInAnalyse };
__property TAddressEvent OnAddressStateChange = {read = FOnAddressStateChange, write = FOnAddressStateChange};
__property TFinishEvent OnFinish = {read = FOnFinished, write = FOnFinished };
__property bool Paused = {read = FPause };
__property bool Terminated = {read = FTerminate };
};
#endif

</pre>




// модуль потока downloader_thread.cpp

<pre>
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "downloader_thread.h"
#pragma package(smart_init)
//****************************************************************************//
//* T_HAS_ADAPTER *//
//****************************************************************************//
//* Ïîòîê äëÿ ñêà÷èâàíèÿ ôàéëà â ëîêàëüíûé ôàéë íà äèñêå *//
//****************************************************************************//
#ifdef _DEBUG
#define StartProc(val) //OutputDebugString(("enter to: "+AnsiString(__FUNC__)+val).c_str())
#define EndProc(val) //OutputDebugString(("exit from: "+AnsiString(__FUNC__)+val).c_str())
#else
#define StartProc(val)
#define EndProc(val)
#endif
#define OutputDebugString(val) ;//


__fastcall T_HAS_Adapter::T_HAS_Adapter(bool aSuspended, TAddressInfo *aInfo)
: TThread(true)
{
Priority = tpIdle;
FInfo = aInfo;
FreeOnTerminate = true;
Msg = "idle";
TempState = aInfo->State;
FIsDownloaded = 0;
State = 0;
RetValue = HA_RV_OK;
try
{
http = new TIdHTTP(NULL);
Suspended = aSuspended;
}catch(...)
{
State = -1;
Terminate();
};
}
//---------------------------------------------------------------------------
__fastcall T_HAS_Adapter::~T_HAS_Adapter()
{
if (http) { delete http; http = NULL; };
};
//---------------------------------------------------------------------------
void __fastcall T_HAS_Adapter::Execute()
{
State = 1;
// while (!Terminated)
{
// if (!Terminated && !FIsDownloaded)
{
State = 2;
InterlockedIncrement(&FIsDownloaded);
AnsiString FUrl = Info->Address->Address;
if (AnsiLowerCase(FUrl).SubString(1, 5) == "http:") // download by web
{
http->Request->UserAgent = "Mozilla/3.0 (compatible";
http->Request->AcceptLanguage = "es-us";
http->ReadTimeout = 30000;

try
{
TFileStream *File = new TFileStream(Info->LocalFileName, fmCreate);
try
{
try
{
http->Get(FUrl, File);
RetValue = HA_RV_OK;
TempState = AS_DOWNLOADED;
}catch(...)
{
try
{
AnsiString Ext = ExtractFileExt(FUrl);
if (Ext == "") http->Get(FUrl+"/", File);
else http->Get(FUrl, File);
RetValue = HA_RV_OK;
TempState = AS_DOWNLOADED;
}catch(...)
{
RetValue = HA_RV_DOWNLOADERROR;
TempState = AS_ERROR;
};
}
}__finally
{
delete File;
};
}catch(...)
{
RetValue = HA_RV_ERRORSAVE;
};
}
else // other protocol
{
RetValue = HA_RV_UNKNOWNPROTOCOL;
/*
SetFileAttributes(FLocalName.c_str(), FILE_ATTRIBUTE_ARCHIVE);
if (CopyFile(FUrl.c_str(), FLocalName.c_str(), false))
{
SetFileAttributes(FLocalName.c_str(), FILE_ATTRIBUTE_ARCHIVE);
RetValue = HA_RV_OK;
}
else
{
RetValue = HA_RV_DOWNLOADERROR;

}
*/ };
State = 2;
InterlockedDecrement(&FIsDownloaded);
Terminate();
};
};

}
//---------------------------------------------------------------------------
/*bool __fastcall T_HAS_Adapter::StartPage(TAddressInfo *Info)
{
if (Info == NULL || Info->LocalFileName == "") return false;
FInfo = Info;
RetValue = HA_RV_START;
// SetEvent(FDownloadEvent);
// if (State == 2) State = 1;
// else
// if (State == 0)
Resume();
return true;
};*/
//---------------------------------------------------------------------------
void __fastcall T_HAS_Adapter::Terminate()
{
TThread::Terminate();
// SetEvent(FDownloadEvent);
};
//---------------------------------------------------------------------------


</pre>



// и его хидер
<pre>
//---------------------------------------------------------------------------

#ifndef Unit2H
#define Unit2H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include "IdHTTP.hpp"

#pragma hdrstop

#include "address.h"

#define HA_ERROR_DOWNLOAD(val) "["+IntToHex(RetValue, 8)+"] Îøèáêà ïðè ñêà÷èâàíèè èëè êîïèðîâàíèè ôàéëà! \n["+FUrl+"]\n\nÈíôîðìàöèÿ äëÿ îòëàäêè:\nôàéë: "+__FILE__+"\nñòðîêà: "+IntToStr(__LINE__)
#define HA_RV_OK 0x0000000
#define HA_RV_START 0x0000001
#define HA_RV_ERRORSAVE 0x0000002
#define HA_RV_UNKNOWNERROR 0x0000003
#define HA_RV_UNKNOWNPROTOCOL 0x0000004
#define HA_RV_OUTOFMEMORY 0x0000005
#define HA_RV_DOWNLOADERROR 0x0181315
typedef enum {AS_INORDER = 0, AS_DOWNLOADING, AS_DOWNLOADED, AS_ANALYZE, AS_READY, AS_ERROR, AS_IDLE } TASState;

extern AnsiString ASStateStr[] = {"In order", "Downloading", "Downloaded", "Analyze", "Ready", "Error"};

class T_HAS_Adapter;

struct TAddressInfo
{
THttpAddress *Address;
TASState State;
AnsiString LocalFileName;
bool Recursive;
T_HAS_Adapter *Thread;
TAddressInfo *Parent;
};

//---------------------------------------------------------------------------
class T_HAS_Adapter : public TThread
{
private:
TAddressInfo *FInfo;
HANDLE FDownloadEvent;
long FIsDownloaded;
protected:
void __fastcall Execute();
public:
int RetValue;
TASState TempState;
AnsiString Msg;
TIdHTTP *http;
int State;
__fastcall T_HAS_Adapter(bool aSuspended, TAddressInfo *aInfo);
virtual __fastcall ~T_HAS_Adapter();
__property TAddressInfo *Info = {read = FInfo, write = FInfo};
// bool __fastcall StartPage(TAddressInfo *Info);
void __fastcall Terminate();
};
//---------------------------------------------------------------------------
#endif
</pre>


и соотв. цикл проверки


//кнопка старт (инициализация)
<pre>
Manager = new THttpDownloadManager(ExtractFilePath(ParamStr(0))+"download");
Manager->OnAddressStateChange = OnAddress;
Manager->StartByDomain("http://localhost/index_f.html");

</pre>

// кнопка очистки памяти от менеджера

<pre>
delete Manager;
Manager = NULL;
ListBox1->Items->Clear();
ListBox2->Items->Clear();

</pre>


// и сам цикл проверки
<pre>
int Cnt = 0;
while (!Application->Terminated)
{
Label2->Caption = IntToStr(++Cnt);
Button1Click(this); // кнопка старт
while (Manager->GetState() < DM_READY && Manager->GetState() != DM_BUSY)
Application->ProcessMessages();
Button2Click(this); // кнопка очистки памяти
Application->ProcessMessages();
};

</pre>


p.s. при всем при этом где-то жрется память, но CodeGuard молчит!
p.s.s. я уже практически в отчаянии, помогите плз!!!!
Re[7]: Многопоточность + синхронизация + как бороться?
От: Alexey Frolov Беларусь  
Дата: 09.03.06 18:02
Оценка:
Здравствуйте, HAS, Вы писали:

HAS>тогда получается надо делать WaitForMultipleObjects, один Event для потока, другой для глобальной остановки, чтобы всегда можно было выйти из цикла в случае глюков, установив событие StopEvent (второе событие)... пральна?

HAS>п.с. подумаю как это сделать...

Да. Правильно. Но чтобы особо не мудрить могу предложить еще вариант. Event может сигнализировать не только об успешно скачанной странице, но и о необходимости завершить поток. Устанавливаем bTerminateNeeded=true и сигналим event, в цикле же первым делом проверяем эту переменную, а потом уже можно сканировать очередь.

HAS>ок, попробовал обернуть все в try catch и в каждом catch воткнул блок OutputDebugString(...) ; DebugBreak(); так вот 60% случаев (может и больше, но не меньше) прога вылетает не в catch, а х.з. где... как тут найти ошибку?


Посмотрел я мельком на код. Жестоко скажу я вам, вряд ли у кого то хватит терпения разобраться. Да еще и с таким форматированием. Может модераторы помогут и запихнут ваш код в соотв тэги.

Оберните в try catch основной код программы, может что-то все таки пропустили. Хотя сильно это не поможет, ну словите вы это exception и все... Но судя по тому что у вас много отладочных сообщений, вы должны хотя бы знать место где происходит обвал, озвучте его

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


тем более этот код можно сделать однопоточным, оттестировать основные функции и переделать на многопоточный вариант.
Re[7]: Многопоточность + синхронизация + как бороться?
От: Slava Antonov Россия http://deadbeef.narod.ru
Дата: 10.03.06 01:03
Оценка:
Hello HAS, you wrote:

> ок, попробовал обернуть все в try catch и в каждом catch воткнул блок OutputDebugString(...) ; DebugBreak(); так вот 60%

случаев (может и больше, но не меньше) прога вылетает не в catch, а х.з. где... как тут найти ошибку?

Я бы для начала попробовал вещи наподобии CodeGuard Билдера.

--
Всего хорошего, Слава
ICQ: 197577902
Posted via RSDN NNTP Server 2.0
Re[8]: Многопоточность + синхронизация + как бороться?
От: TSS Россия http://www.sdl.ru
Дата: 10.03.06 10:00
Оценка:
Здравствуйте, 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]: Многопоточность + синхронизация + как бороться?
От: Vadim B  
Дата: 11.03.06 17:44
Оценка:
Здравствуйте, HAS, Вы писали:

HAS>количество потоков я ограничиваю некой величиной MAXTHREAD ( <= 10), т.е. если в работе MAXTHREAD потоков, то другие мы не запускаем


Все равно, запуск потока — дорогая операция.

VB>>Лучшим будет решение с пулом потоков, каждый из которых в цикле выбирает адреса страниц из глобального списка, скачивает страницу и анализирует результат. Соответственно, синхронизация нужна только при доступе к этому списку, ну и при помещении результата куда надо, конечно. Количество потоков легко регулировать в соответствии с загрузкой системой и количеством процессоров. Вариант — два пула потоков, один скачивает страницы, другой обрабатывает. Примерно так работают веб-сервера.


HAS>не совсем понял что значит два пула потоков... как это выглядит (архитектурно — структурно)


Есть два списка:
1. список адресов, которые надо скачать
2. список скачанных страниц, которые надо обработать

Есть две функции:
1. первая в бесконечном цикле выбирает очередной элемент из первого списка (ждет, если ничего нет), скачивает страницу, помещает результат во второй список
2. вторая в бесконечном цикле выбирает очередной элемент из второго списка (ждет, если ничего нет) и обрабатывает страницу.

Запускается N потоков с функцией 1 в качестве потоковой функции, и M потоков с функцией 2. N и M можно регулировать независимо (первые в основном расходуют сетевые ресурсы, вторые — процессорное время).

На самом деле, вместо первых можно использовать один поток, запуская операции скачивания асинхронно.
Re[8]: Многопоточность + синхронизация + как бороться?
От: HAS Россия hasalex@mail.ru
Дата: 13.03.06 05:46
Оценка:
Здравствуйте, Alexey Frolov, Вы писали:

AF>Здравствуйте, HAS, Вы писали:



HAS>>ок, попробовал обернуть все в try catch и в каждом catch воткнул блок OutputDebugString(...) ; DebugBreak(); так вот 60% случаев (может и больше, но не меньше) прога вылетает не в catch, а х.з. где... как тут найти ошибку?


AF>Посмотрел я мельком на код. Жестоко скажу я вам, вряд ли у кого то хватит терпения разобраться. Да еще и с таким форматированием. Может модераторы помогут и запихнут ваш код в соотв тэги.


согласен, тяжело... попытался обрамить код в тэг <pre>, не проканало

AF>Оберните в try catch основной код программы, может что-то все таки пропустили. Хотя сильно это не поможет, ну словите вы это exception и все... Но судя по тому что у вас много отладочных сообщений, вы должны хотя бы знать место где происходит обвал, озвучте его


все обернуто

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


AF>тем более этот код можно сделать однопоточным, оттестировать основные функции и переделать на многопоточный вариант.


пробовал...
Re[8]: Многопоточность + синхронизация + как бороться?
От: HAS Россия hasalex@mail.ru
Дата: 13.03.06 05:47
Оценка:
Здравствуйте, Slava Antonov, Вы писали:

SA>Hello HAS, you wrote:


>> ок, попробовал обернуть все в try catch и в каждом catch воткнул блок OutputDebugString(...) ; DebugBreak(); так вот 60%

SA>случаев (может и больше, но не меньше) прога вылетает не в catch, а х.з. где... как тут найти ошибку?

SA>Я бы для начала попробовал вещи наподобии CodeGuard Билдера.


так он прикручен, все галочки выставлены... молчит зараза!

SA>--

SA>Всего хорошего, Слава
SA>ICQ: 197577902
Re[9]: Многопоточность + синхронизация + как бороться?
От: HAS Россия hasalex@mail.ru
Дата: 13.03.06 05:47
Оценка:
Здравствуйте, TSS, Вы писали:

TSS>Здравствуйте, Slava Antonov, Вы писали:


>>> ок, попробовал обернуть все в try catch и в каждом catch воткнул блок OutputDebugString(...) ; DebugBreak(); так вот 60%

SA>>случаев (может и больше, но не меньше) прога вылетает не в catch, а х.з. где... как тут найти ошибку?

TSS>выкинуть все try — catch нафиг и ловить exception.

TSS>как появился -- цепляешься отладчиком и отлаживаешь.

это как? в смысле как поймать без try catch?
Re: Причина глюка найдена. Как ее устранить?
От: HAS Россия hasalex@mail.ru
Дата: 13.03.06 05:56
Оценка:
Итак, проблема найдена — все дело в компоненте TIdHTTP (Indy 9.0), который начинает глючить когда происходит скачивание большого числа страниц ( > 1000). Ощущение что либо он что-то там не до закрывает и система начинает ругаться вследствие ограниченности ресурсов, либо в нем самом я что-то не так делаю ((

вся работа с компонентом ограничена кодом:

TIdHTTP *http = new TIdHTTP(NULL);
http->Request->UserAgent = "Mozilla/3.0 (compatible";
http->Request->AcceptLanguage = "es-us";
http->ReadTimeout = 30000;
http->Get(Data);
delete http;

все это в потоке... попробовал на его место воткнуть TNMHTTP, та же история... когда же заменяю просто копированием файла (тестирую на локальном сервере, т.е. меняю http://localhost... на c:\\www\www...)? то все работает замечтательно! сайт из 6666 страниц в цикле скачивался 23 раза и за все время ни разу не было ни одного глюка...

Вопрос: как победить подобные сетевые прихоти?! (
Re[2]: Причина глюка найдена. Как ее устранить?
От: Vadim B  
Дата: 13.03.06 08:28
Оценка:
Здравствуйте, 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]: Многопоточность + синхронизация + как бороться?
От: TSS Россия http://www.sdl.ru
Дата: 13.03.06 10:21
Оценка:
Здравствуйте, HAS, Вы писали:

>>>> ок, попробовал обернуть все в try catch и в каждом catch воткнул блок OutputDebugString(...) ; DebugBreak(); так вот 60%

SA>>>случаев (может и больше, но не меньше) прога вылетает не в catch, а х.з. где... как тут найти ошибку?

TSS>>выкинуть все try — catch нафиг и ловить exception.

TSS>>как появился -- цепляешься отладчиком и отлаживаешь.

HAS>это как? в смысле как поймать без try catch?


Все подобные ошибки я обычно отлавливаю именно так. Т.е. убираю всю обработку исключений (кроме тех которые являются "запланированным", например _com_error-ы) и, при возникновении исколючений, цепляю отладчик и просматриваю стеки потоков.
Могу предположить, что у тебя производится работа с памятью, которая уже была освобождена, например автоматические переменные на стеке потока (а поток-то вышел) и т.п.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Signed, [TSS] /SDL/
Re[3]: Причина глюка найдена. Как ее устранить?
От: HAS Россия hasalex@mail.ru
Дата: 13.03.06 11:09
Оценка:
Здравствуйте, 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)
Re[4]: Причина глюка найдена. Как ее устранить?
От: aset  
Дата: 13.03.06 11:18
Оценка:
Здравствуйте, HAS, Вы писали:

HAS>Здравствуйте, 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);
       delete http;


Скорее всего могут исчерпаться хэндлы, GetLastError() не пробовали вызывать?
Re[5]: Причина глюка найдена. Как ее устранить?
От: HAS Россия hasalex@mail.ru
Дата: 13.03.06 15:21
Оценка:
Здравствуйте, 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. смотрел по ТаскМанагеру, число используемых хэндлов всегда в неких жестких пределах (т.е. то прибавится, то убавится, но всегда в каких-то жестких рамках)
Re[4]: Причина глюка найдена. Как ее устранить?
От: Vadim B  
Дата: 13.03.06 19:10
Оценка:
Здравствуйте, HAS, Вы писали:

VB>>Вопрос: можно ли с помощью этой компоненты скачивать множество страниц (по порядку, одну за другой), не создавая объект каждый раз заново? И будет ли при этом он по-прежнему глючить?


HAS>даже если и можно, вариант одна за другой в одном потоке не годится...


Ну почему же не годится? Я выше по теме рассказал об архитектуре, которую я считаю правильной — в ней как раз каждый поток запускается один раз на все время работы приложения и в нем в цикле выполняется извлечение адресов из списка, скачивание страниц и запись их в другой список. Ну впрочем, мое дело — посоветовать, твое дело — отказаться .
Re[11]: Многопоточность + синхронизация + как бороться?
От: Slava Antonov Россия http://deadbeef.narod.ru
Дата: 14.03.06 02:20
Оценка:
Hello TSS, you wrote:

> Все подобные ошибки я обычно отлавливаю именно так. Т.е. убираю всю обработку исключений (кроме тех которые являются "запланированным", например _com_error-ы) и, при возникновении исколючений, цепляю отладчик и просматриваю стеки потоков.


Не знаю как у вас, а скажем отладчик Delphi и Builder ловит все исключения, даже которые обрабатываются самой программой. Поэтому смысла убирать try/catch — не вижу.

--
Всего хорошего, Слава
ICQ: 197577902
Posted via RSDN NNTP Server 2.0
Re[12]: Многопоточность + синхронизация + как бороться?
От: TSS Россия http://www.sdl.ru
Дата: 14.03.06 08:34
Оценка:
Здравствуйте, 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]: Многопоточность + синхронизация + как бороться?
От: Slava Antonov Россия http://deadbeef.narod.ru
Дата: 14.03.06 09:17
Оценка:
Hello TSS, you wrote:

> Простите за вопрос, а Вы еще какие-нибудь отладчики, кроме багландовских знаете?


Знаю, но какое это отношение имеет к теме?

> В MSVS, например, есть возможность выставить исключения, которые должен ловить отладчик, если его не поймала программа.


И?
Пиписьками давайте в другом месте меряться.

ЗЫ: Отладчики Delphi и Builder тоже позволяют указывать какие исключения нам нужны, а какие нет.

--
Всего хорошего, Слава
ICQ: 197577902
Posted via RSDN NNTP Server 2.0
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.