Форум: "Основная";
Текущий архив: 2005.06.29;
Скачать: [xml.tar.bz2];
ВнизКак избежать гонок в потоках Найти похожие ветки
← →
leonidus © (2005-06-01 11:56) [40]>evvcom про try/finally понял, про грамотный код то же ясно:) но где его взять, что бы не увязнуть в массе лишнего кода и выделить только то, что нужно мне
← →
evvcom © (2005-06-01 12:02) [41]
> но где его взять
В исходниках Borland или их Demo.
> что бы не увязнуть в массе лишнего кода и выделить только
> то, что нужно мне
Надо тренироваться разглядывать именно то, что нужно. IDE Delphi очень удобно для этого сделана. В последствии это умение очень пригодится, когда, устроившись на работу, на тебя упадет задача по доработке чужого и, возможно, далеко не "грамотного" кода.
← →
leonidus © (2005-06-01 13:40) [42]Ок, но вот теперь всплыла такая загвоздка. Написанный выше код, подразумевает последовательные операции записи и чтения. Т.е. сначала все потоки поотдельности запишут информацию в массив а потом мы из массива организуем чтение. Однако в реальной задаче нужно будет читать в произвольный момент времени (т.е. конечно можно подождать пока данный пишущий поток отработает, но ждать пока отработают все пишущие потоки нельзя) и потом запустить процедуру чтения из основного потока программы. Как быть в такой ситуации?
← →
КаПиБаРа © (2005-06-01 13:54) [43]Можно попробовоть так
Читающий поток синхронизируется WaitForSingleObject(MutexRead)
Пишущий поток WaitForMultipleObject(MutexRead и MutexWrite) причем нужно MutexRead освобождать раньше чем MutexWrite
← →
evvcom © (2005-06-01 14:02) [44]
> Т.е. сначала все потоки поотдельности запишут информацию
> в массив а потом мы из массива организуем чтение.
Написанный выше код, не подразумевает такого. Чтение происходит всякий раз с вызовом showresult
← →
Игорь Шевченко © (2005-06-01 14:50) [45]
> Однако в реальной задаче нужно будет читать в произвольный
> момент времени (т.е. конечно можно подождать пока данный
> пишущий поток отработает, но ждать пока отработают все пишущие
> потоки нельзя) и потом запустить процедуру чтения из основного
> потока программы.
Каждый поток при записи выполняет код:
CS.Enter;
try
inc(nom);
a[nom] := somedata;
finally
CS.Leave;
end;
Читающий поток выполняет код:
CS.Enter;
try
for I:=1 to nom do
что-то с a[I];
finally
CS.Leave;
end;
На время выполнения чтения все пишущие потоки блокируются.
← →
leonidus © (2005-06-01 15:14) [46]Игорь, а тогда такой вопрос, у читающих потока будет какой-то приоритет перед записывающими чтоли? Я почему спаршиваю, мне не понятно как ваш код решит задачу когда скажем 10 потоков выстроились в очередь на запись в массив и поочередно входят в критическую секцию. В какой-то момент главному потоку программы нужно считать из массива данные, главный поток также входит в свою критическую секцию... и что просходит далее, текущий записывающий поток доработает и начнет работу читающий поток? если так то почему именно читающий, ведь в очереди еще стоят и записывающие потоки у них тоже есть право воспользоваться массивом.
← →
evvcom © (2005-06-01 15:36) [47]
> leonidus © (01.06.05 15:14) [46]
Какому из потоков выделит квоту времени ОС заранее неизвестно, да это и не важно. Важно только то, что после последнего пишущего в результате synchronize отработает читающий.
← →
Игорь Шевченко © (2005-06-01 15:47) [48]
> В какой-то момент главному потоку программы нужно считать
> из массива данные, главный поток также входит в свою критическую
> секцию... и что просходит далее, текущий записывающий поток
> доработает и начнет работу читающий поток? если так то почему
> именно читающий, ведь в очереди еще стоят и записывающие
> потоки у них тоже есть право воспользоваться массивом.
Вообще-то на то она и очередь, чтобы обслуживать запросы в порядке поступления. Можно для себя решить, что главнее, чтение или запись, и, соответственно, изменять приоритеты потоков, например, если пишущие важнее, то читающий поток будет ждать, пока все, кто хочет записать, не освободят массив. И наоборот, если важно чтение (что маловероятно), то читающий поток будет обладать большим правом на захват массива.
← →
leonidus © (2005-06-01 15:50) [49]т.е. нужно после отработки каждого потока вызывать 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;
← →
leonidus © (2005-06-01 15:55) [50]Игорь, вы предлагаете динамично менять приоритеты потоков, но ведь можно обойтись и без этого, я думал что код в [49] решит такую задачу. А на счет что главнее, мне как раз важнее что бы у меня была возможность в любой момент считать данные из массива, т.к. эти данные (по сути URL-ссылки) подаются на вход блока скачивания (см. [1]) а результаты парсинга (новые URL ссылки) записываются в массив.
← →
evvcom © (2005-06-01 16:01) [51]По моему ты этот код именно в таком виде уже приводил. Да типа того, правильно. Только надо еще добавить form1.memo1.Lines.Clear; а то увидишь несколько копий того, что хотел увидеть с "нарастающим итогом".
Недостаток. Увидишь Memo, мерцающий столько раз сколько раз он будет вызван. Чтобы этого избежать, не делать Clear и добавлять в Memo только добавленный в текущем потоке a[i]
← →
Alexander Panov © (2005-06-01 16:06) [52]leonidus © (01.06.05 15:55) [50]
мне как раз важнее что бы у меня была возможность в любой момент считать данные из массива
Для этого используется флажок:
- Читающий поток взводит флаг.
- Пишущий поток проверяет флаг, и если он взведен - поток ничего не пишет и не входит в критическую секцию.
либо(как выше было написано) у читающего потока нужно повысить приоритет.
← →
evvcom © (2005-06-01 16:22) [53]
> Для этого используется флажок:
А это еще зачем? Сама критическая секция уже и есть нужный "флажок".
> мне как раз важнее что бы у меня была возможность в любой
> момент считать данные из массива
Да нормально ты все считаешь. Вход в крит.секцию, запись в массив, выход из секции займет миллионные, если не меньше, доли секунды, ты даже ничего и не заметишь.
← →
Игорь Шевченко © (2005-06-01 16:54) [54]
> нужно после отработки каждого потока вызывать synchronize
> который по сути передает управление главному потоку и если
> главному потокe к данному моменту потребовалось считать
> данные из массива
Ты уж определись, что и как тебе нужно. Либо каждый пишущий поток оповещает читателя, что он что-то записал, либо читающий поток читает независимо от пишущих, либо что-то третье.
← →
Alexander Panov © (2005-06-01 17:45) [55]evvcom © (01.06.05 16:22) [53]
А это еще зачем? Сама критическая секция уже и есть нужный "флажок".
Нет, н флажок.
Критическую секцию равноправно могут использовать как пишущий, так и читающий потоки.
← →
leonidus © (2005-06-01 19:35) [56]>Игорь я это вижу так: пишущие потоки стоят в очереди и по одному в соответствии с очередью входят в критическую секцию и записывают в массив свою порцию данных. Закончив запись управление через synchronize передается главному потоку программы который определяет нужно ему читать или нет (т.е. если считанная до этого порция ссылок не скачалась за новой не идем, если скачалась, нужно обратиться к массиву за новой порцией ссылок). В случае если ничего читать не надо процедура которую мы вызвали из synchronize завершается и массив отдается на запись записывающим потокам, если же из массива что-то нужно считать, чтение производится через критическую секцию, и только после этого процедура завершается передавая доступ к массиву пишущим потокам. И тут сразу вопрос, если ничего читать не надо, что достаточно просто скачем через goto перейти в конец процедуры и записывающая очередь сама получит управления и начнет работать или приостановленную очередь надо как-то специально запустить на продолжение? или я все усложняю?
← →
leonidus © (2005-06-01 19:42) [57]А на счет флагов мне то же не ясно. Флаг я нужен для сигнализации между пищущими и читающими потоками. Но если пищущий или читающий поток уже вошел в критическую секцию, другой просто в нее войти не сможет, выходит тут уже как бы и так передается сигнал занятости.
Alexander Panov © (01.06.05 17:45) [55]
Критическую секцию равноправно могут использовать как пишущий, так и читающий потоки.
Это да, но я так понял, что кто первый тот и молодец. Первый будет записывающий поток, после его завершения передаем управление главному, а записывающие стоят, главный или читает или нет, но все равно в конце передает доступ к массиву записывающим потокам, точнее тому перед кем очередь была остановлена.
← →
Polevi © (2005-06-01 20:17) [58]>записывающая очередь сама получит управления и начнет работать или приостановленную очередь надо как-то специально запустить на продолжение
получит управление один из потоков ожидающих освобождения критической секции после после того как отработавший поток вызовет LeaveCritivalSection
← →
Alexander Panov © (2005-06-01 21:01) [59]leonidus © (01.06.05 19:42) [57]
...Первый будет записывающий поток, после его завершения передаем управление главному...
Тебе ведь нужно, чтобы приоритетным был читающий поток?
В случае с равноправными потоками(а основной поток тоже является таковым - пусть он будет читающим - R) R при необходимости чтения данных будет пытаться войти в критическую секцию. Если в это время то же самое пытаются сделать остальные пишущие потоки(W), то начать выполняться может любой поток.
Для гарантированного получения времени процессора по требованию у R либо должен быть выше приоритет, либо(как я писал выше), необходимо, чтобы R устанавливал флажок "Читаю", который W должны проверять.
W проверяет флажок. Если флажок установлен в "Читаю", W не пытается входить в критическую секцию, а переходит в состояние ожидания, пока флажок не будет сброшен, соответственно W и не записывает данные в это время.
Но R после установки флажка "Читаю" должен также дождаться, пока текущий W закончит записывать данные, если таковой существует, т.е. для этого необходим тоже некий флаг.
Для таких флагов подходят как семафоры, так и мьютексы.
← →
Игорь Шевченко © (2005-06-01 22:12) [60]А количество пишущих потоков фиксировано или их может быть произвольное число ? И от чего это зависит ?
← →
leonidus © (2005-06-01 22:41) [61]>Alexander Panov ©
Ок теперь я понял для чего флаги. Дело в том, что мне бы хотелось всю черную работу возложить на ОС, она лучше меня справится с разделением квантов процессорного времени:) Наверное что бы не запутаться с флагами поставлю разные приориты у читающего и пищущего потоков. А достаточно сделать различия в один уровень или надо больше?
>Игорь Шевченко ©
Кол-во пишущих потоков может быть от одного до думаю нескольких десятков. А зависит это от того сколькими потоками будет вестись скачивание документов, на каждый скачиваемый документ свой парсер результаты работы которого (новые ссылки) будут писатся в массив.
И так, хочется всетаки подвести итог, этого досольно мошного треда. Используем код приведенный выше (самый последний, т.е. он самый правильный), к этому делу добавляем приоритет на читающий и записывающий потоки и вроде все должно работать. Но вот подскажите как нагляднее реализовать тестовое приложение (без скачивания страниц конечно), что бы был хорошо виден процесс работы и читающих и пишущих потоков? Хочется написать маленькую программку, провести нагрузочный тест, а потом алгоритм перенести непосредственно в мое приложение.
← →
Alexander Panov © (2005-06-01 23:09) [62]leonidus © (01.06.05 22:41) [61]
Наверное что бы не запутаться с флагами поставлю разные приориты у читающего и пищущего потоков. А достаточно сделать различия в один уровень или надо больше?
При повышении приоритета учти, что у тебя есть еще и другие процессы в системе.
← →
Alexander Panov © (2005-06-01 23:10) [63]На память не помню, имеет ли основной поток равные приоритеты с дополнительными.
← →
Игорь Шевченко © (2005-06-01 23:55) [64]leonidus © (01.06.05 22:41) [61]
> А достаточно сделать различия в один уровень или надо больше?
Достаточно. Причем, поток, работа которого важна, должен работать с нормальным приоритетом, а не очень важные потоки - с пониженным, чтобы не мешать потокам остальных процессов в системе.
Пример многопоточного приложения можно взять, например, у меня на сайте:
http://www.schevchenko.net.ru/SRC/SuperMarket_50.zip
Прямой аналогии с твоей задачей, разумеется, в примере нет, но взаимодействие потоков имеется.
Удачи!
← →
evvcom © (2005-06-02 08:51) [65]
> Alexander Panov © (01.06.05 17:45) [55]
> evvcom © (01.06.05 16:22) [53]
> А это еще зачем? Сама критическая секция уже и есть нужный
> "флажок".
>
> Нет, н флажок.
> Критическую секцию равноправно могут использовать как пишущий,
> так и читающий потоки.
...
> Для таких флагов подходят как семафоры, так и мьютексы.
Зачем на крит.секции еще и семафоры с мьютексами городить? Чем они реально помогут? Будут те же WaitForSingleObject как в читающем, так и пишущем потоках. Результат тот же, те же остановки и переключения на другой поток, только будет использовано 2 объекта синхронизации. Зачем все это? Для данной задачи вполне хватит одной крит. секции.
> Но R после установки флажка "Читаю" должен также дождаться,
> пока текущий W закончит записывать данные, если таковой
> существует, т.е. для этого необходим тоже некий флаг.
Во-во. И еще десяток флагов надо добавить.
2 leonidus: не вижу смысла даже менять приоритеты потоков. Парсинг будет выполнятся в любом случае горазде быстрее, чем подкачка данных из интернета. Поэтому в пишущем потоке я бы даже synchronize не стал вызывать, а использовал бы PostMessage. Сообщил основному потоку о завершении закачки, выбрал из очереди очередной линк и опять на закачку, если линки кончились, то suspend. Скорее всего тебе одного доп.потока хватит для парсинга результатов от всех потоков закачки, т.к. количество скачанного все равно будет лимитироваться шириной твоего интернет-канала. Не надо усложнять там, где этого не требуется.
← →
Alexander Panov © (2005-06-02 09:37) [66]evvcom © (02.06.05 8:51) [65]
Ты разве еще не понял, что читающий поток может вообще не получить управления?
← →
Alexander Panov © (2005-06-02 09:39) [67]evvcom © (02.06.05 8:51) [65]
Зачем на крит.секции еще и семафоры с мьютексами городить
Затем, что критическая секция не поможет тебе выбрать нужный поток для приоритетного выполнения.
evvcom © (02.06.05 8:51) [65]
Во-во. И еще десяток флагов надо добавить.
Вообще-то это делается при необходимости. А ты можешь добавлять хоть 2 десятка.
evvcom © (02.06.05 8:51) [65]
Результат тот же, те же остановки и переключения на другой поток, только будет использовано 2 объекта синхронизации. Зачем все это? Для данной задачи вполне хватит одной крит. секции.
Не вводи в заблуждение автора вопроса.
← →
Digitman © (2005-06-02 09:45) [68]
> Alexander Panov © (01.06.05 23:10) [63]
> На память не помню, имеет ли основной поток равные приоритеты
> с дополнительными
имеет.
основной создается системой с THREAD_PRIORITY_NORMAL по дифолту
дополнительные, если не приложить свои шаловливые ручки к потрохам CreateThread, создаются с тем же приоритетом по дифолту
The thread is created with a thread priority of THREAD_PRIORITY_NORMAL (цитата из справки к CreateThread)
← →
evvcom © (2005-06-02 10:05) [69]
> Ты разве еще не понял, что читающий поток может вообще не
> получить управления?
Пальцем можно ткнуть, что помешает?
> Затем, что критическая секция не поможет тебе выбрать нужный
> поток для приоритетного выполнения.
Но я нигде не встречал и того, что семафоры и мьютексы помогают "выбрать нужный поток для приоритетного выполнения". Для этого меняют приоритет самого потока. Можно и здесь пальцем ткнуть, если считаешь, что я не прав. Обсудим.
← →
Alexander Panov © (2005-06-02 10:57) [70]evvcom © (02.06.05 10:05) [69]
Пальцем можно ткнуть, что помешает?
Достаточно простого примера.
У тебя сотня потоков. Из них один читающий, 99 пишущих.
Предположим, для записи каждому пишущему потоку нужно 1 сек.
Все потоки запросили критическую секцию.
В какой момент читающий поток сможет получить эту критическую секцию?
Ответ такой - это неизвестно. он может начать выполняться сразу, а может и через минуту.
Но возможно через минуту его действия уже потеряют актуальность, и программа просто будет терминирована недождавшимся ответа пользователем.
Но я нигде не встречал и того, что семафоры и мьютексы помогают "выбрать нужный поток для приоритетного выполнения". Для этого меняют приоритет самого потока. Можно и здесь пальцем ткнуть, если считаешь, что я не прав. Обсудим.
А никто и не говорил, что объекты синхронизации изменяют приоритеты. Они лишь помогают выстроить логику программы так, как нужно тебе.
Установка приоритета гарантирует тебе то, что нужный поток будет получать больше квантов времени для выполнения.
Причем повышенный приоритет потока может тебе "помочь" наоборот.
В данном конкретном случае без использования объектов синхронизации поток с повышенным приоритетом будет использовать максимально возможное количество времени процессора, что приведет к "голоданию" остальных потоков.
← →
evvcom © (2005-06-02 11:30) [71]
> > Ты разве еще не понял, что читающий поток может вообще не
> > получить управления?
> evvcom © (02.06.05 10:05) [69]
> Пальцем можно ткнуть, что помешает?
>
> Достаточно простого примера.
C примером согласен. Но если
> программа просто будет терминирована недождавшимся ответа
> пользователем
то это уже форс-мажор, и этим я бы не стал все же объяснять выражение "вообще не получить управления". А чтобы не нервировать пользователя, я уже писал, что крит.секциями стоит защищать быстро выполняемые куски кода, чтобы не тормозить работу остальных потоков, использующих эти же данные. В примере автора запись происходит всего в 2 строчках кода, их мы и защищаем. Аналогично с чтением.
> поток с повышенным приоритетом будет использовать максимально
> возможное количество времени процессора, что приведет к
> "голоданию" остальных потоков
Так ведь есть вариант с понижением приоритета. О нем, кажется, Игорь упомянул. И все же я не увидел объяснения целесообразности употребления доп. флажков (семафора, мьютекса) в данном примере.
← →
Alexander Panov © (2005-06-02 11:52) [72]evvcom © (02.06.05 11:30) [71]
Так ведь есть вариант с понижением приоритета. О нем, кажется, Игорь упомянул. И все же я не увидел объяснения целесообразности употребления доп. флажков (семафора, мьютекса) в данном примере.
А я не увидел предложенного решения от тебя(методологии, алгоритм). Или я что-то упустил?
Применение объектов синхронизации(мьютексов, событий, семафоров) в классе данных задач - с арбитражем конкурирующих за ресурс потоков(несколько писателей, один читатель, или подобных) - стандартный прием.
А проблему с захватом процессорного времени одним потокм ты уже решил?
← →
evvcom © (2005-06-02 12:19) [73]
> А я не увидел предложенного решения от тебя(методологии,
> алгоритм). Или я что-то упустил?
Предложения не сформулированы в одном моем посте, это надо перечитывать все обсуждение. А алгоритм подправленный всеми автор выложил в итоге сам (тоже в нескольких постах).
> А проблему с захватом процессорного времени одним потокм
Здесь нет этой проблемы, Имхо. И здесь потоки держат критическую секцию не по 1 секунде. Даже у сотни потоков на разовую запись уйдет меньше секунды.
По-моему, мы углубились в такие дебри и пытаемся раздуть из мухи слона там, где проблема выеденного яйца не стоит. Все имхо.
← →
Alexander Panov © (2005-06-02 12:26) [74]evvcom © (02.06.05 12:19) [73]
По-моему, мы углубились в такие дебри и пытаемся раздуть из мухи слона там, где проблема выеденного яйца не стоит. Все имхо.
Это не дебри.
Это элементарная тщательность при проектировании многопоточного приложения.
То, что запись происходит бысто - это не аргумент.
evvcom © (02.06.05 12:19) [73]
Здесь нет этой проблемы, Имхо. И здесь потоки держат критическую секцию не по 1 секунде. Даже у сотни потоков на разовую запись уйдет меньше секунды.
А читающий поток?
← →
Alexander Panov © (2005-06-02 12:29) [75]>evvcom ©
При проектировании многопоточного потока не бывает мелочей, на которые не стоит обращать внимание.
Любая мелочь может сыграть роковую роль для приложения.
-------------
PS.
А потом говорим, что приложение падает непонятно почему.
← →
evvcom © (2005-06-02 12:50) [76]
> А читающий поток?
А что читающий поток? Ни один из потоков не захватывает время настолько, чтобы заметно мешать другим потокам. И после того как пишущий делает свое дело, он извещает об этом читателя. Это гарантирует, что читающий обработает все данные.
> Любая мелочь может сыграть роковую роль для приложения.
С этим полностью согласен.
← →
Digitman © (2005-06-02 12:55) [77]
> Alexander Panov
> evvcom
не кажется ли вам. уважаемые судари, что сабж перешел в статус "мерянье концами" ?)
← →
Alexander Panov © (2005-06-02 13:19) [78]evvcom © (02.06.05 12:50) [76]
И после того как пишущий делает свое дело, он извещает об этом читателя.
Как извещает? И что делает в это время читающий поток?
← →
evvcom © (2005-06-02 13:54) [79]
> Как извещает?
мне нравится PostMessage, хотя по ситуации
> И что делает в это время читающий поток?
Во время оповещения читающий поток ждет, когда ему ОС выделит квант времени. :)
> не кажется ли вам.
Мне это уже давно кажется. Надо завязывать.
← →
Erik1 © (2005-06-02 15:17) [80]Всегда тут не по теме треплются.
Автору
Советую обратить внимание на группу функций WaitFor(MultiObject). Для опредерения необходимости запуска потоков. Допустим такую ситуацию, пишуший поток вошол в критическаю секцию и завис на обашении к сети. Вся наша система встала, даже трудно понять, что произошло. Для облегчения взвимодействия потоков применяются события и функции ожидающие этих событий. Причем событий может быть несколько, например сигнал аваийного звершения задачи.
P.S
Свой код могу выложить или выслать по почте.
Страницы: 1 2 3 вся ветка
Форум: "Основная";
Текущий архив: 2005.06.29;
Скачать: [xml.tar.bz2];
Память: 0.66 MB
Время: 0.042 c