2010-07-12 3 views
7

, поэтому у меня есть булевский тип в C++ на многопроцессорной машине. Переменная начинается с жизни как истина, а затем есть несколько потоков, любая из которых может записать ее как ложную.Нужно ли защищать эту переменную блокировкой?

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

Единственный способ, которым мне нужен замок, - если на очень низком уровне память может быть повреждена двумя конкурирующими записями. Если, например, инструкция сборки на процессоре A записывает 0 в байт, который представляет логическое значение, в то время как процессор B делает то же самое ... и вместо того, чтобы записать 0, память заканчивается значением 22 или что нибудь. Это может что-то испортить.

Итак, в общем случае, если proc A записывает 3 в ячейку памяти, в то время как proc B записывает 7 без синхронизации, я гарантированно получаю хотя бы 3 или 7? Или это легко сломать память?

Редактировать:

Спасибо за комментарии ребята. Дополнительная информация: в программе, конечно, есть синхронизация. Подводя итог, соответствующий флаг говорит о том, что определенный пул памяти «грязный» (необходимо уплотнять). Любой поток может, следовательно, принять решение об установке этого флага в значение false (это означает, что пул грязный). Например, освобождение памяти из пула делает ее грязной. Любой поток также может считывать этот флаг и устанавливать другой флаг, чтобы сигнализировать о необходимости очистки - эта проверка выполняется, когда память распределяется из пула, очистка сигнализируется, если у нас мало памяти. Где-то в моем главном критическом разделе между итерациями, где каждый поток идет искать больше данных для обработки, у меня будут потоки, проверяющие этот второй флаг, и сделайте что-то подходящее, чтобы убедиться, что: все остальные адды заканчивают свою текущую итерацию, один поток очищает память, устанавливает первый флаг в true (как в пуле не загрязнен), устанавливает второй флаг обратно в false и затем снова освобождает все потоки.

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

Я думаю, что тот же аргумент применим ко второму знаку, упомянутому выше.

+1

Из интереса, почему вы не заботитесь о чтении? Предположим, что какой-то поток написал false - тогда, если вам все равно, читает ли результат в 'true' или' false', зачем читать чтение? Это просто, что «неправильные» «истинные» чтения безвредны? –

ответ

7

Если вы только проверяете состояние переменной и устанавливаете ее в значение false в одном направлении, вам не о чем беспокоиться, за исключением того, что некоторые из потоков могут немного запоздать, чтобы увидеть, что переменная уже установлена ​​на ложный. (это может быть в некоторой степени преодолено с помощью ключевого слова «volatile».) Затем два потока могут установить значение false, что не представляет проблемы, поскольку для переменной задано одно значение в одном направлении. Скажем, логическая запись в ячейку памяти не гарантируется атомарной, какой вред? Конечное значение, которое они оба будут писать, будет одинаковым.

Тем не менее, вы должны использовать метод блокировки, если:

  • установка Значение не только один-направление: вы установите его в ложное, а затем обратно к истине, а затем снова ложь, и т.д.
  • Вы принимаете какое-либо зависимое действие от информации, в которой поток установил значение в значение false. Потому что, очевидно, могут быть два победителя.
3

Это простой способ сломать вещи. С логическим, вы можете быть в порядке в большинстве случаев, но не предоставляются никаких гарантий.

У вас есть два варианта: использовать мьютексы (блокировки) или использовать атомные примитивы. Атомные примитивы будут использовать аппаратные инструкции для выполнения теста и устанавливать операции в потокобезопасном режиме, не требуя фактического мьютекса и более легкого решения. Компилятор GNU обеспечивает доступ к атомным операциям через расширения, специфичные для архитектуры. Существуют также переносные атомные операционные библиотеки; библиотека Glib C предоставляет атомные операции, которые возвращаются к использованию мьютекса, если атомарные примитивы недоступны, хотя это довольно тяжелая библиотека со многими другими функциями.

Существует библиотека Boost.Atomic, которая абстрагирует атомные операции для C++; основанный на его имени, похоже, что он направлен на включение в библиотеку C++ библиотеки Boost, но еще не сделал этого.

1

Для двоих, как правило, нет ответа, мьютекс не нужен, но (как отметил Майкл Э) все может случиться, поэтому вам, скорее всего, нужно будет больше узнать о своей арке, прежде чем принимать такое решение. Другое примечание: для кода, возможно, все еще потребуется блокировка общей логики, связанной с bool, особенно если bool читается более одного раза в ходе логики подпрограммы.

Некоторые большие блоги, которые я читал, чтобы держать меня на многопоточных пальцах:

http://blogs.msdn.com/b/nativeconcurrency/

http://herbsutter.com/2009/04/20/effective-concurrency-use-thread-pools-correctly-keep-tasks-short-and-nonblocking/

наилучшими пожеланиями,

9

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

Опять же, на аппаратном обеспечении можно уйти только с маркировкой, что однократное булева переменная volatile (который был объявлен бесполезным для параллельного программирования кстати), чтобы предотвратить компилятор от оптимизации его в регистр, , но только если вы действительно дон Не заботьтесь о заказе писем.

Позвольте мне вновь перебирать это с чековой списка:

  • Вы готовы делать потерять некоторые обновления, что булевы?
  • Уверены ли вы, что нет других обновлений памяти, которые пришли до boolean flip в исходном коде, но может быть переупорядочено после, что флип все испортит?
  • Вы уверены, что вас не интересует порядок событий в вашем приложении?

Если у вас есть три сильных «да» ответы, может уйти с не защищая этот флаг. Все еще рассмотрите возможность вставки перед запоминанием переменной памяти и перед тем, как записать ее, выпустить защитный барьер памяти. Мое предположение, однако, состояло бы в том, чтобы переосмыслить дизайн и выложить четкие синхронные межпоточные коммуникации и последовательность событий.

Надеюсь, это поможет.

+0

Отличный ответ, спасибо за упоминание согласованности кеша. –

+0

1) Вы говорите, что теряете обновления для этого булева ... вы имеете в виду, что аппаратное обеспечение игнорирует запись? Я так не думаю, поэтому я не уверен, что вы имеете в виду (вы могли видеть мое добавление выше) 2) да 3) некоторые вещи, о которых я забочусь. Заметьте, я никогда не читаю и не пишу этот флаг в том же блоке кода. всегда читайте один раз или пишите один раз. - По этой причине синхронизация здесь кажется бесполезной. Я думаю, мы нашли применение для летучих! :) – Scott

+0

также важно отметить, что я не моделирую сценарий «подождать ..» где угодно. Я никогда не хочу, чтобы нить ждал ложного, или ждать чего-нибудь. go go go! – Scott

1

Вы спрашиваете о двух вещах.

Сначала вы спрашиваете об атомарности присвоения bool. Нет никакой гарантии, что логическое назначение будет атомарным. На практике это обычно, но вы не должны полагаться на это. Некоторая странная архитектура может реализовывать назначение bool во многих машинных инструкциях ...

Вторичный запрос о повреждении данных из-за параллельной записи - на практике переход от процессора к памяти осуществляется с помощью шины, которая почти всегда содержит больше бит чем примитивные типы, над которыми вы работаете. Таким образом, такая коррупция может произойти для очень странной архитектуры или при работе с большими числами (не поддерживается системой изначально). На практике вы обычно получаете 3 или 7. Но опять же вы не можете полагаться на это.

В заключение этого - вам нужен замок.

1

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

В более общих чертах вы, вероятно, сочтете более простым и быстрым использование мьютексов в рутинном порядке, чем поцарапать голову, если вы хотите уйти без этого на этот раз. Добавление одного, где это не нужно, не вызывает ошибки; оставляя один, где это возможно.

Смежные вопросы