Есть поток, проверяющий несколько раз в секунду некий буловский флаг, и если флаг выставлен, поток завершается.
В то же время некий другой поток естественно однажды этот флаг выставит.
Вопрос: если не делать синхронизацию с помощью критических секций, не случится ли чего плохого, если один поток будет читать флаг в то время как второй будет его выставлять? Вероятность этого конечно весьма и весьма мала (особенно учитывая современные процы), но тем не менее..
Тут же возникает более общий вопрос: а вообще интовый ресурс нужно синхронизировать, если меняет его только один поток, а читают многие?
Здравствуйте, Zline, Вы писали:
Z>Вопрос видимо классический, но тем не менее:
Z>Есть поток, проверяющий несколько раз в секунду некий буловский флаг, и если флаг выставлен, поток завершается. Z>В то же время некий другой поток естественно однажды этот флаг выставит.
Только один вопрос: чем плох мютекс?
... << RSDN@Home 1.1.4 beta 7 rev. 447>>
WARNING: expression "to_be || !to_be" is always true
Здравствуйте, Zline, Вы писали:
Z>Вопрос видимо классический, но тем не менее:
Z>Есть поток, проверяющий несколько раз в секунду некий буловский флаг, и если флаг выставлен, поток завершается. Z>В то же время некий другой поток естественно однажды этот флаг выставит.
Z>Вопрос: если не делать синхронизацию с помощью критических секций, не случится ли чего плохого, если один поток будет читать флаг в то время как второй будет его выставлять? Вероятность этого конечно весьма и весьма мала (особенно учитывая современные процы), но тем не менее..
Z>Тут же возникает более общий вопрос: а вообще интовый ресурс нужно синхронизировать, если меняет его только один поток, а читают многие?
Если не хочется использовать объекты ядра, можно использовать IntelockedIncrement/InterlockedDecrement
оверквотинг был несколько лишний в принципе...
С>Если не хочется использовать объекты ядра, можно использовать IntelockedIncrement/InterlockedDecrement
...но ответ должен быть именно такой. +1!
... << RSDN@Home 1.1.4 beta 7 rev. 447>>
Valery A. Boronin, RSDN Team, linkedin.com\in\boronin
R&D Mgmt & Security. AppSec & SDL. Data Protection and Systems Programming. FDE, DLP, Incident Management. Windows Filesystems and Drivers.
Z>>Есть поток, проверяющий несколько раз в секунду некий буловский флаг, и если флаг выставлен, поток завершается. Z>>В то же время некий другой поток естественно однажды этот флаг выставит.
A>Только один вопрос: чем плох мютекс?
Как истинный ариец, отвечу вопросом на вопрос — а чем в данном случае он хорош?
Здравствуйте, Zline, Вы писали: Z>Тут же возникает более общий вопрос: а вообще интовый ресурс нужно синхронизировать, если меняет его только один поток, а читают многие?
Если тебе нужен просто вариант "триггера", то можно не синхронизировать.
Даже если обстоятельства позже изменятся и писать в переменную будет не один поток, а несколько, то даже в этом случае вполне можно обойтись без строгой синхронизации.
Поскольку интересен только факт "ноль-не ноль".
Есть только одна потенциальная засада, переменная должна быть объявлена volatile, что от неприятностей с оптимизацией отнюдь не гарантирует.(см. одноимённый многостраницный топик). Я бы всё-таки присоединился к автору, посоветовавшему использовать Interlocked* функции. По той причине, что они содержат примитив, исполняющий роль mfence и может избавить от потенциальных недоразумений с синхронизацией процессорных кэшей.
Пользоваться тяжёлыми методами с привлечением объектов ядра я никакого смысла не вижу.
Здравствуйте, Amidlokos, Вы писали:
Z>>Вопрос видимо классический, но тем не менее: Z>>Есть поток, проверяющий несколько раз в секунду некий буловский флаг, и если флаг выставлен, поток завершается. Z>>В то же время некий другой поток естественно однажды этот флаг выставит. A>Только один вопрос: чем плох мютекс?
Тем, что на его создание и обращение к нему требуется, согласно Рихтеру, на много больше времени, нежели при работе с критическими секциями.
Здравствуйте, Zline, Вы писали:
Z>Вопрос видимо классический, но тем не менее:
И так же классические грабли
Z>Есть поток, проверяющий несколько раз в секунду некий буловский флаг, и если флаг выставлен, поток завершается. Z>В то же время некий другой поток естественно однажды этот флаг выставит.
Кроме того, что он проверяет этот флаг, поток еще что-нибудь делает? В любом случае, я бы воспользовался событиями (CreateEvent / WaitForSingleObject).
Z>Тут же возникает более общий вопрос: а вообще интовый ресурс нужно синхронизировать, если меняет его только один поток, а читают многие?
Спасибо всем за ответы. Вопрос даже не в том мьютекс/критическая секция/события, а в том стоит ли париться с этим (вставляя соотв. код во все обращения) — ресурс то интовый, безхитростный
Наверно остановлюсь на InterLocked-функциях. Спасибо за совет.
Z>Наверно остановлюсь на InterLocked-функциях. Спасибо за совет.
Эм, сори что снова поднимаю тему, но возник еще вопрос: а те потоки, что будут только читать переменную, должны делать какие-либо дополнительные телодвижения? или просто localVar = sharedVar; ?
В поисках ответа я наткнулся на ряд дискуссий по поводу того, нужно ли такую переменную объявить volatile, а также на идею, заключающуюся в том, что все переменные, используемые несколькими потоками должны быть volatile. Так ли это? А если так, то что делать со списковыми структурами, которые юзают много потоков (синхронизация через критические секции конечно есть, а нужен ли тут еще volatile?)
Z>Вопрос: если не делать синхронизацию с помощью критических секций, не случится ли чего плохого, если один поток будет читать флаг в то время как второй будет его выставлять?
Ничего плохого не случится. Критические секции тут АБСОЛЮТНО не нужны.
Z>>Наверно остановлюсь на InterLocked-функциях. Спасибо за совет. Z>Эм, сори что снова поднимаю тему, но возник еще вопрос: а те потоки, что будут только читать переменную, должны делать какие-либо дополнительные телодвижения? или просто localVar = sharedVar; ?
Z>>>Наверно остановлюсь на InterLocked-функциях. Спасибо за совет. Z>>Эм, сори что снова поднимаю тему, но возник еще вопрос: а те потоки, что будут только читать переменную, должны делать какие-либо дополнительные телодвижения? или просто localVar = sharedVar; ?
AB>InterLockedExchange?
Чтобы просто читать, никаких примитивов синхронизации не нужно. Модификатор volatile — нужен.
Z>>Вопрос: если не делать синхронизацию с помощью критических секций, не случится ли чего плохого, если один поток будет читать флаг в то время как второй будет его выставлять?
S> Ничего плохого не случится. Критические секции тут АБСОЛЮТНО не нужны.
AS>>Как истинный ариец, отвечу вопросом на вопрос — а чем в данном случае он хорош?
A>Тем, что таких вопросов не возникнет...
Зато возникнут вопросы у того, кто будет просматривать такой код. Вопросы о квалификации того, кто это писал. Не следует использовать объекты ядра где ни попадя.
Здравствуйте, Andrew S, Вы писали:
AS>Зато возникнут вопросы у того, кто будет просматривать такой код. Вопросы о квалификации того, кто это писал. Не следует использовать объекты ядра где ни попадя.
Межпотоковая синхронизация это как скобки. Лучше поставить лишнюю пару, чем не поставить там где надо.
Здравствуйте, Andrew S, Вы писали:
AB>>InterLockedExchange?
угу, но оно меняет и флаг, а оно мне при чтении не нужно
AS>Чтобы просто читать, никаких примитивов синхронизации не нужно. Модификатор volatile — нужен.
Насчет флага понятно, а теперь volatile =)
Ну допустим флагам я его поставлю. А списковым структурам, которые юзает несколько тредов, как прописывать volatile? всем указателям в структуре и корню структуры?
Здравствуйте, Zline, Вы писали:
Z>Ну допустим флагам я его поставлю. А списковым структурам, которые юзает несколько тредов, как прописывать volatile? всем указателям в структуре и корню структуры?
Здравствуйте, adontz, Вы писали:
A>Здравствуйте, Zline, Вы писали:
Z>>Ну допустим флагам я его поставлю. А списковым структурам, которые юзает несколько тредов, как прописывать volatile? всем указателям в структуре и корню структуры? A>
AS>>Зато возникнут вопросы у того, кто будет просматривать такой код. Вопросы о квалификации того, кто это писал. Не следует использовать объекты ядра где ни попадя.
A>Межпотоковая синхронизация это как скобки. Лучше поставить лишнюю пару, чем не поставить там где надо.
Плохое сравнение. Как раз в случае синхронизации надо просчитывать каждую "скобку", поскольку лишняя может настолько убить перфоманс приложения, что и многопоточность будет только лишней. Опять же, есть случаи, когда использование кернел объектов для синхронизации не только нежелательно, но и просто невозможно. За примером ходить далеко не надо — тот же синглтон.
Z>>Ну допустим флагам я его поставлю. А списковым структурам, которые юзает несколько тредов, как прописывать volatile? всем указателям в структуре и корню структуры? A>
A>volatile list<int>
A>
A>?
При работе с std списками надо использовать критические секции, соответственно, никакой volatile не нужен. volatile в этом смысле применим лишь к встроенным интегральным типам.
Z>По идее при изменении списка естесн меняется только job *next, его и описать как volatile job *next; ?
Я бы использовал тут критические секции для синхронизации доступа к списку. Если очень важна производительность или критические секции использовать невозможно по каким-либо причинам, я бы почитал статьи по "безблокирующим" способам доступа к разделяемым ресурсам. Например, для затравки http://www.cs.chalmers.se/~tsigas/papers/rtcsa99.pdf
Там все более сложно, чем вам кажется
Z>>По идее при изменении списка естесн меняется только job *next, его и описать как volatile job *next; ?
AS>Я бы использовал тут критические секции для синхронизации доступа к списку. Если очень важна производительность или критические секции использовать невозможно по каким-либо причинам, я бы почитал статьи по "безблокирующим" способам доступа к разделяемым ресурсам. Например, для затравки http://www.cs.chalmers.se/~tsigas/papers/rtcsa99.pdf AS>Там все более сложно, чем вам кажется
доступ естественно синхронизируется с помощью критических секций, вопрос в том, нужно ли помимо этого еще объявлять поле job *next как volatile ?
Z>доступ естественно синхронизируется с помощью критических секций, вопрос в том, нужно ли помимо этого еще объявлять поле job *next как volatile ?
Нет. Критические секции выступают в роли барьера памяти, так что никаких модификаторов тут не надо.
Z>>Наверно остановлюсь на InterLocked-функциях. Спасибо за совет.
Z>Эм, сори что снова поднимаю тему, но возник еще вопрос: а те потоки, что будут только читать переменную, должны делать какие-либо дополнительные телодвижения? или просто localVar = sharedVar; ?
Да должен. Иначе словишь глюку с синхронизацией процессорных кэшей на многопроцессорных тачках. Удобнее всего для этого использовать функцию InterlockedExchangeAdd(..., 0).
Здравствуйте, TarasCo, Вы писали:
TC>Здравствуйте, sercher, Вы писали:
Z>>>Вопрос: если не делать синхронизацию с помощью критических секций, не случится ли чего плохого, если один поток будет читать флаг в то время как второй будет его выставлять?
S>> Ничего плохого не случится. Критические секции тут АБСОЛЮТНО не нужны.
TC>АБСОЛЮТНО согласен....
Случится, еще как случится — на многопроцессорной тачке. Достаточно в считывающем потоке флагу попасть в процессорный кэш, чтобы он не заметил его изменения.
Здравствуйте, mkopachev, Вы писали:
M>Здравствуйте, TarasCo, Вы писали:
TC>>Здравствуйте, sercher, Вы писали:
Z>>>>Вопрос: если не делать синхронизацию с помощью критических секций, не случится ли чего плохого, если один поток будет читать флаг в то время как второй будет его выставлять?
S>>> Ничего плохого не случится. Критические секции тут АБСОЛЮТНО не нужны.
TC>>АБСОЛЮТНО согласен....
M> Случится, еще как случится — на многопроцессорной тачке. Достаточно в считывающем потоке флагу попасть в процессорный кэш, чтобы он не заметил его изменения.
M> С уважением Михаил Копачев
Процессорный кеш имеет свойство синхронизхироваться с памятью.... и с другими процессорными кешами
Если интересует задержка в несколько тактов между установкой флага одним процессором и чтением другим — тогда Вы правы, читающий поток может пролететь мимо флага, но при работе в ОС с вытесняющей многозадачностью, я Вас умоляю, даже если поток, исполняющийся на другом процессоре получит обнавленный флаг хоть через 1 мс ( что по меньшей мере фантастично ) — это не критично, для Windows даже эта величина намного меньше кванта, соответсвенно ее можно считать = 0.
Критично, если из-за оптимизации флаг станет регистровой переменной, тогда возможен дедлок. Но тут поможет volatile .
Z>>Эм, сори что снова поднимаю тему, но возник еще вопрос: а те потоки, что будут только читать переменную, должны делать какие-либо дополнительные телодвижения? или просто localVar = sharedVar; ?
M> Да должен. Иначе словишь глюку с синхронизацией процессорных кэшей на многопроцессорных тачках. Удобнее всего для этого использовать функцию InterlockedExchangeAdd(..., 0).
Нет, не должен. Если для записи значения пользуются InterlockedXxx функции, то кеши процессоров синхронизированы. Соответственно, чтение завсегда дает правильный результат.
Здравствуйте, TarasCo, Вы писали:
TC>Здравствуйте, mkopachev, Вы писали:
M>>Здравствуйте, TarasCo, Вы писали:
TC>>>Здравствуйте, sercher, Вы писали:
Z>>>>>Вопрос: если не делать синхронизацию с помощью критических секций, не случится ли чего плохого, если один поток будет читать флаг в то время как второй будет его выставлять?
S>>>> Ничего плохого не случится. Критические секции тут АБСОЛЮТНО не нужны.
TC>>>АБСОЛЮТНО согласен....
M>> Случится, еще как случится — на многопроцессорной тачке. Достаточно в считывающем потоке флагу попасть в процессорный кэш, чтобы он не заметил его изменения.
M>> С уважением Михаил Копачев
TC>Процессорный кеш имеет свойство синхронизхироваться с памятью.... и с другими процессорными кешами TC>Если интересует задержка в несколько тактов между установкой флага одним процессором и чтением другим — тогда Вы правы, читающий поток может пролететь мимо флага, но при работе в ОС с вытесняющей многозадачностью, я Вас умоляю, даже если поток, исполняющийся на другом процессоре получит обнавленный флаг хоть через 1 мс ( что по меньшей мере фантастично ) — это не критично, для Windows даже эта величина намного меньше кванта, соответсвенно ее можно считать = 0.
Любопытная точка зрания. Но дело в том, что я на эти грабли уже наступал, так что с Вами я не соглашусь. Для того чтобы процессорный кэш синхронизировался, нужно принимать специальные меры, в частности префикс lock у команд (кстати именно так все Interlocked — функции и реализуются). Чтобы не разводить флейм просто сошлюсь на руководство по системному программированию процессора Intel — там это все есть.
Здравствуйте, Andrew S, Вы писали:
Z>>>Эм, сори что снова поднимаю тему, но возник еще вопрос: а те потоки, что будут только читать переменную, должны делать какие-либо дополнительные телодвижения? или просто localVar = sharedVar; ?
M>> Да должен. Иначе словишь глюку с синхронизацией процессорных кэшей на многопроцессорных тачках. Удобнее всего для этого использовать функцию InterlockedExchangeAdd(..., 0).
AS>Нет, не должен. Если для записи значения пользуются InterlockedXxx функции, то кеши процессоров синхронизированы. Соответственно, чтение завсегда дает правильный результат.
Согласен.
Есть правда один тонкий момент — начальное значение переменной — нужно присваивать явно через Interloced, если не делать этого явно, то читать нужно тоже через Interlocked.
TC>>Процессорный кеш имеет свойство синхронизхироваться с памятью.... и с другими процессорными кешами TC>>Если интересует задержка в несколько тактов между установкой флага одним процессором и чтением другим — тогда Вы правы, читающий поток может пролететь мимо флага, но при работе в ОС с вытесняющей многозадачностью, я Вас умоляю, даже если поток, исполняющийся на другом процессоре получит обнавленный флаг хоть через 1 мс ( что по меньшей мере фантастично ) — это не критично, для Windows даже эта величина намного меньше кванта, соответсвенно ее можно считать = 0.
M> Любопытная точка зрания. Но дело в том, что я на эти грабли уже наступал, так что с Вами я не соглашусь. Для того чтобы процессорный кэш синхронизировался, нужно принимать специальные меры, в частности префикс lock у команд (кстати именно так все Interlocked — функции и реализуются). Чтобы не разводить флейм просто сошлюсь на руководство по системному программированию процессора Intel — там это все есть.
Это типа RTFM? Возможно, Вы наступили не на грабли а на лопату
Естественно, если префикс lock не используется есть промежуток времени, когда кеши не когерентны. В некоторых случая это чревато ( допустим, счетчик ссылок — можно как недосчитаться, так и пересчитаться ). Но в случае, когда один поток ( процессор ) устанавливает флаг, а другой на это реагирует ( распространенный случай — прекращает свою работу ) такая рассинхронизация кешей не страшна. Самый "страшный" случай — читающий поток среагирует на флаг позже.
AS>>Нет, не должен. Если для записи значения пользуются InterlockedXxx функции, то кеши процессоров синхронизированы. Соответственно, чтение завсегда дает правильный результат.
M> Согласен. M> Есть правда один тонкий момент — начальное значение переменной — нужно присваивать явно через Interloced, если не делать этого явно, то читать нужно тоже через Interlocked.
Смотря какая переменная. Если global static — то не надо, это значение заполняется на этапе компиляции и когерентно после загрузки модуля. Если function static — тогда стандарт С++ определяет, что такая переменная инициализируется до вызова этой функции. Но на деле все компиляторы поступают с ней аналогично global static. Если переменная на стеке или в хипе — тогда да, возможен, хотя и совсем невероятен вариант, когда другой тред не увидит правильного ее значения. Я бы не стал на этом акцентировать внимание, тем более что функция создания треда наверняка синхронизирует кеши, уж больно тяжелая это операция.
Здравствуйте, Zline, Вы писали:
Z>Вопрос видимо классический, но тем не менее:
Z>Есть поток, проверяющий несколько раз в секунду некий буловский флаг, и если флаг выставлен, поток завершается. Z>В то же время некий другой поток естественно однажды этот флаг выставит.
Z>Вопрос: если не делать синхронизацию с помощью критических секций, не случится ли чего плохого, если один поток будет читать флаг в то время как второй будет его выставлять? Вероятность этого конечно весьма и весьма мала (особенно учитывая современные процы), но тем не менее..
Z>Тут же возникает более общий вопрос: а вообще интовый ресурс нужно синхронизировать, если меняет его только один поток, а читают многие?
Эта тема обсуждалась много раз — срочно в поиск! Например, здесь
Кратко: на x86 архитектуре — для булевой переменной можно обойтись без синхронизации. Для выровненного int — тоже. Для невыровненного — надо блокировать шину, т.е. пользоваться Interlocked*. На других архитектурах (например, Итаниум) — возможны нюансы.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>
It's kind of fun to do the impossible (Walt Disney)