![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Как избежать гонок в потоках [D6, WinXP]
leonidus © (2005-05-31 10:38:00)Мастера подскажите пожалуйста. Я написал небольшую программу, она скачивает новости с разных сайтов, и затем парсит каждую скачанную страницу на предмет нужной информации. Т.е. программа состоит из бвух блоков - блок закачки (качается сразу в несколько потоков, для каждой страницы с новостями свой поток) и блок парсинга (в нем также предусмотрена многопоточность, при этом для каждой скачанной страницы должен запускаться свой экземпляр одного и тогоже потока). Результаты парсинга (заголовки новостей) помещаются в массив. И вот в чем проблема. Если одновременно скачаются две страницы и одновременно начнут работать два парсера, то может так получиться, что они будут обращаться к одному элементу массив, что бы записать в него полученный заголовок новости. Т.е. я тап понимаю это ситуация гонок. Подскажите как этого избежать. Может есть какие-то статьи по этому поводу, я что-то ничего толком не нашел. Заранее спасибо.
Alexander Panov (2005-06-02 09:39:00) [21]evvcom © (02.06.05 8:51) [65]
Зачем на крит.секции еще и семафоры с мьютексами городить
Затем, что критическая секция не поможет тебе выбрать нужный поток для приоритетного выполнения.
evvcom © (02.06.05 8:51) [65]
Во-во. И еще десяток флагов надо добавить.
Вообще-то это делается при необходимости. А ты можешь добавлять хоть 2 десятка.
evvcom © (02.06.05 8:51) [65]
Результат тот же, те же остановки и переключения на другой поток, только будет использовано 2 объекта синхронизации. Зачем все это? Для данной задачи вполне хватит одной крит. секции.
Не вводи в заблуждение автора вопроса.
Alexander Panov (2005-06-02 09:37:00) [22]evvcom © (02.06.05 8:51) [65]
Ты разве еще не понял, что читающий поток может вообще не получить управления?
evvcom (2005-06-02 08:51:00) [23]
> Alexander Panov © (01.06.05 17:45) [55]
> evvcom © (01.06.05 16:22) [53]
> А это еще зачем? Сама критическая секция уже и есть нужный
> "флажок".
>
> Нет, н флажок.
> Критическую секцию равноправно могут использовать как пишущий,
> так и читающий потоки.
...
> Для таких флагов подходят как семафоры, так и мьютексы.
Зачем на крит.секции еще и семафоры с мьютексами городить? Чем они реально помогут? Будут те же WaitForSingleObject как в читающем, так и пишущем потоках. Результат тот же, те же остановки и переключения на другой поток, только будет использовано 2 объекта синхронизации. Зачем все это? Для данной задачи вполне хватит одной крит. секции.
> Но R после установки флажка "Читаю" должен также дождаться,
> пока текущий W закончит записывать данные, если таковой
> существует, т.е. для этого необходим тоже некий флаг.
Во-во. И еще десяток флагов надо добавить.
2 leonidus: не вижу смысла даже менять приоритеты потоков. Парсинг будет выполнятся в любом случае горазде быстрее, чем подкачка данных из интернета. Поэтому в пишущем потоке я бы даже synchronize не стал вызывать, а использовал бы PostMessage. Сообщил основному потоку о завершении закачки, выбрал из очереди очередной линк и опять на закачку, если линки кончились, то suspend. Скорее всего тебе одного доп.потока хватит для парсинга результатов от всех потоков закачки, т.к. количество скачанного все равно будет лимитироваться шириной твоего интернет-канала. Не надо усложнять там, где этого не требуется.
Игорь Шевченко (2005-06-01 23:55:00) [24]leonidus © (01.06.05 22:41) [61]
> А достаточно сделать различия в один уровень или надо больше?
Достаточно. Причем, поток, работа которого важна, должен работать с нормальным приоритетом, а не очень важные потоки - с пониженным, чтобы не мешать потокам остальных процессов в системе.
Пример многопоточного приложения можно взять, например, у меня на сайте:
http://www.schevchenko.net.ru/SRC/SuperMarket_50.zip
Прямой аналогии с твоей задачей, разумеется, в примере нет, но взаимодействие потоков имеется.
Удачи!
Alexander Panov (2005-06-01 23:10:00) [25]На память не помню, имеет ли основной поток равные приоритеты с дополнительными.
Alexander Panov (2005-06-01 23:09:00) [26]leonidus © (01.06.05 22:41) [61]
Наверное что бы не запутаться с флагами поставлю разные приориты у читающего и пищущего потоков. А достаточно сделать различия в один уровень или надо больше?
При повышении приоритета учти, что у тебя есть еще и другие процессы в системе.
leonidus (2005-06-01 22:41:00) [27]>Alexander Panov ©
Ок теперь я понял для чего флаги. Дело в том, что мне бы хотелось всю черную работу возложить на ОС, она лучше меня справится с разделением квантов процессорного времени:) Наверное что бы не запутаться с флагами поставлю разные приориты у читающего и пищущего потоков. А достаточно сделать различия в один уровень или надо больше?
>Игорь Шевченко ©
Кол-во пишущих потоков может быть от одного до думаю нескольких десятков. А зависит это от того сколькими потоками будет вестись скачивание документов, на каждый скачиваемый документ свой парсер результаты работы которого (новые ссылки) будут писатся в массив.
И так, хочется всетаки подвести итог, этого досольно мошного треда. Используем код приведенный выше (самый последний, т.е. он самый правильный), к этому делу добавляем приоритет на читающий и записывающий потоки и вроде все должно работать. Но вот подскажите как нагляднее реализовать тестовое приложение (без скачивания страниц конечно), что бы был хорошо виден процесс работы и читающих и пишущих потоков? Хочется написать маленькую программку, провести нагрузочный тест, а потом алгоритм перенести непосредственно в мое приложение.
Игорь Шевченко (2005-06-01 22:12:00) [28]А количество пишущих потоков фиксировано или их может быть произвольное число ? И от чего это зависит ?
Alexander Panov (2005-06-01 21:01:00) [29]leonidus © (01.06.05 19:42) [57]
...Первый будет записывающий поток, после его завершения передаем управление главному...
Тебе ведь нужно, чтобы приоритетным был читающий поток?
В случае с равноправными потоками(а основной поток тоже является таковым - пусть он будет читающим - R) R при необходимости чтения данных будет пытаться войти в критическую секцию. Если в это время то же самое пытаются сделать остальные пишущие потоки(W), то начать выполняться может любойпоток.
Для гарантированного получения времени процессора по требованию у R либо должен быть выше приоритет, либо(как я писал выше), необходимо, чтобы R устанавливал флажок "Читаю", который W должны проверять.
W проверяет флажок. Если флажок установлен в "Читаю", W не пытаетсявходить в критическую секцию, а переходит в состояние ожидания, пока флажок не будет сброшен, соответственно W и не записывает данные в это время.
Но R после установки флажка "Читаю" должен также дождаться, пока текущий W закончит записывать данные, если таковой существует, т.е. для этого необходим тоже некий флаг.
Для таких флагов подходят как семафоры, так и мьютексы.
Polevi (2005-06-01 20:17:00) [30]>записывающая очередь сама получит управления и начнет работать или приостановленную очередь надо как-то специально запустить на продолжение
получит управление один из потоков ожидающих освобождения критической секции после после того как отработавший поток вызовет LeaveCritivalSection
leonidus (2005-06-01 19:42:00) [31]А на счет флагов мне то же не ясно. Флаг я нужен для сигнализации между пищущими и читающими потоками. Но если пищущий или читающий поток уже вошел в критическую секцию, другой просто в нее войти не сможет, выходит тут уже как бы и так передается сигнал занятости.
Alexander Panov © (01.06.05 17:45) [55]
Критическую секцию равноправно могут использовать как пишущий, так и читающий потоки.
Это да, но я так понял, что кто первый тот и молодец. Первый будет записывающий поток, после его завершения передаем управление главному, а записывающие стоят, главный или читает или нет, но все равно в конце передает доступ к массиву записывающим потокам, точнее тому перед кем очередь была остановлена.
leonidus (2005-06-01 19:35:00) [32]>Игорь я это вижу так: пишущие потоки стоят в очереди и по одному в соответствии с очередью входят в критическую секцию и записывают в массив свою порцию данных. Закончив запись управление через synchronize передается главному потоку программы который определяет нужно ему читать или нет (т.е. если считанная до этого порция ссылок не скачалась за новой не идем, если скачалась, нужно обратиться к массиву за новой порцией ссылок). В случае если ничего читать не надо процедура которую мы вызвали из synchronize завершается и массив отдается на запись записывающим потокам, если же из массива что-то нужно считать, чтение производится через критическую секцию, и только после этого процедура завершается передавая доступ к массиву пишущим потокам. И тут сразу вопрос, если ничего читать не надо, что достаточно просто скачем через goto перейти в конец процедуры и записывающая очередь сама получит управления и начнет работать или приостановленную очередь надо как-то специально запустить на продолжение? или я все усложняю?
Alexander Panov (2005-06-01 17:45:00) [33]evvcom © (01.06.05 16:22) [53]
А это еще зачем? Сама критическая секция уже и есть нужный "флажок".
Нет, н флажок.
Критическую секцию равноправно могут использовать как пишущий, так и читающий потоки.
Игорь Шевченко (2005-06-01 16:54:00) [34]
> нужно после отработки каждого потока вызывать synchronize
> который по сути передает управление главному потоку и если
> главному потокe к данному моменту потребовалось считать
> данные из массива
Ты уж определись, что и как тебе нужно. Либо каждый пишущий поток оповещает читателя, что он что-то записал, либо читающий поток читает независимо от пишущих, либо что-то третье.
evvcom (2005-06-01 16:22:00) [35]
> Для этого используется флажок:
А это еще зачем? Сама критическая секция уже и есть нужный "флажок".
> мне как раз важнее что бы у меня была возможность в любой
> момент считать данные из массива
Да нормально ты все считаешь. Вход в крит.секцию, запись в массив, выход из секции займет миллионные, если не меньше, доли секунды, ты даже ничего и не заметишь.
Alexander Panov (2005-06-01 16:06:00) [36]leonidus © (01.06.05 15:55) [50]
мне как раз важнее что бы у меня была возможность в любой момент считать данные из массива
Для этого используется флажок:
- Читающий поток взводит флаг.
- Пишущий поток проверяет флаг, и если он взведен - поток ничего не пишет и не входит в критическую секцию.
либо(как выше было написано) у читающего потока нужно повысить приоритет.
evvcom (2005-06-01 16:01:00) [37]По моему ты этот код именно в таком виде уже приводил. Да типа того, правильно. Только надо еще добавить form1.memo1.Lines.Clear; а то увидишь несколько копий того, что хотел увидеть с "нарастающим итогом".
Недостаток. Увидишь Memo, мерцающий столько раз сколько раз он будет вызван. Чтобы этого избежать, не делать Clear и добавлять в Memo только добавленный в текущем потоке a[i]
leonidus (2005-06-01 15:55:00) [38]Игорь, вы предлагаете динамично менять приоритеты потоков, но ведь можно обойтись и без этого, я думал что код в [49] решит такую задачу. А на счет что главнее, мне как раз важнее что бы у меня была возможность в любой момент считать данные из массива, т.к. эти данные (по сути URL-ссылки) подаются на вход блока скачивания (см. [1]) а результаты парсинга (новые URL ссылки) записываются в массив.
leonidus (2005-06-01 15:50:00) [39]т.е. нужно после отработки каждого потока вызывать synchronize который по сути передает управление главному потоку и если главному потокe к данному моменту потребовалось считать данные из массива, то он это может спокойно сделать в критической секции, после чего продолжат работу пишущие потоки. Так? Выше приведенный код в таком случае будет в виде:
procedure TThread_work.showresult;
var
i:integer;
begin
FCS.Enter;
try
for i:=1 to nom do
begin
form1.memo1.Lines.Add(a[i]);
end;
finally
FCS.Leave;
end;
form1.memo1.Lines.Add('---');
end;
Игорь Шевченко (2005-06-01 15:47:00) [40]
> В какой-то момент главному потоку программы нужно считать
> из массива данные, главный поток также входит в свою критическую
> секцию... и что просходит далее, текущий записывающий поток
> доработает и начнет работу читающий поток? если так то почему
> именно читающий, ведь в очереди еще стоят и записывающие
> потоки у них тоже есть право воспользоваться массивом.
Вообще-то на то она и очередь, чтобы обслуживать запросы в порядке поступления. Можно для себя решить, что главнее, чтение или запись, и, соответственно, изменять приоритеты потоков, например, если пишущие важнее, то читающий поток будет ждать, пока все, кто хочет записать, не освободят массив. И наоборот, если важно чтение (что маловероятно), то читающий поток будет обладать большим правом на захват массива.