Неустойчивость и многопоточность: является ли поточно-безопасным?
Предположим, что существуют два потока, выполняющих Thread1()
и Thread2()
соответственно. Нить 1 просто устанавливает глобальный флаг, чтобы сообщить, что поток 2 прекратит работу, а поток 2 периодически проверяет, должен ли он выйти.
volatile bool is_terminate = false;
void Thread1()
{
is_terminate = true;
}
void Thread2()
{
while (!is_terminate) {
// ...
}
}
Я хочу спросить, безопасен ли этот код в предположении, что доступ к is_terminate
является атомарным. Я уже знаю, что многие материалы утверждают, что volatile
не может обеспечить безопасность потока вообще. Но в ситуации, когда разделяется только одна атомная переменная, действительно ли нам нужно защитить общую переменную с помощью блокировки?
Ответы
Ответ 1
Вероятно, это похоже на потокобезопасность.
Безопасность потоков зависит от контекста. Обновление bool всегда безопасно для потоков, если вы никогда не читаете его.
И если вы прочитаете его, тогда ответ будет зависеть от того, когда вы прочтете его, и что означает прочитанное.
На некоторых процессорах, но не на всех, запись в объект типа bool
будет атомарной. x86 CPU, как правило, делают его атомарным, но другие могут и не быть. Если обновление не является атомарным, добавление volatile
не поможет вам.
Но следующей проблемой является переупорядочение. Компилятор (и ЦП) будет выполнять чтение/запись переменных volatile
в указанном порядке без какого-либо переупорядочения. Так что хорошо.
Но он не дает никаких гарантий относительно переупорядочения одного доступа к памяти volatile
по сравнению со всеми не volatile
. Настолько обычным примером является то, что вы определяете какой-то флаг для защиты доступа к ресурсу, вы делаете флаг volatile
, а затем компилятор перемещает доступ к ресурсу таким образом, что это происходит до того, как вы проверите флаг. Это позволило сделать это, поскольку оно не переупорядочивало внутренний порядок двух обращений volatile
, а просто a volatile
и не volatile
.
Честно говоря, вопрос, который я задал, - это не просто делать это правильно?
Возможно, что volatile
будет работать в этой ситуации, но почему бы не спасти себя от неприятностей и не дать понять, что это правильно? Вместо этого сломайте барьер памяти.
Ответ 2
Он не является потокобезопасным.
Если потоки, например, запускаются на процессорах с отдельными кэшами, нет языковых правил, говорящих, что кеши должны быть синхронизированы при записи изменчивой переменной. Другой поток может не видеть изменения в течение очень долгого времени, если когда-либо.
Чтобы ответить другим способом:
Если volatile
достаточно, чтобы быть потокобезопасным, почему С++ 0x добавляет целую главу с атомными операциями?
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2047.html
Ответ 3
Во-первых, volatile используется для отключения оптимизации компиляции в c/С++. см. этот для понимания изменчивости.
Ядром атома является выравнивание слов и размер is_terminate, если размер is_terminate меньше, чем собственный размер машины и выровнен, тогда R и W являются атомарными.
В вашем контексте, с или без volatile, thread2 может читать старое значение после того, как thread1 изменил его, но thread2 может его прочитать в конце концов.
Если в конечном итоге для вас будет нормально читать, то ваши коды будут безопасными для потоков.
Ответ 4
это безопасно, потому что один поток только читает, а один только пишет.
Потоки на самом деле не разделяют этот флаг, один читает, один пишет. У вас не может быть гонки, потому что другой поток никогда не напишет плохой результат, и поток чтения никогда не прочитает плохой результат. простой.
Ответ 5
Нет, это не так. Это может быть потокобезопасным , если доступ к значениям был атомарным, но в С++ вы не можете предположить, что доступ к переменным является потокобезопасным, если вы не используете какие-либо специфические для компилятора конструкции или примитивы синхронизации.
Ответ 6
Это все еще небезопасно. Вы должны использовать синхронизацию для доступа к is_terminate
Доступ к bool не гарантируется как атомная операция.
Ответ 7
Я считаю, что этот код безопасен, пока оба потока не будут писать bool
(уже вы упомянули, что доступ к значениям атомный).
Ответ 8
Большая проблема с предположением, что ключевое слово volatile
накладывает любые виды безопасности потоков, заключается в том, что стандарты C или С++ не имеют понятия потоков в описываемой ими абстрактной машине.
Гарантии, которые стандарт накладывает на ключевое слово volatile
, действительны только внутри, а не между несколькими потоками.
Это дает разработчикам полную свободу делать то, что им нравится, когда дело касается нитей. Если они выбрали ключевое слово volatile
для обеспечения безопасности в потоках, то вам повезло. Чаще всего это не так.
Ответ 9
Этот код не является потокобезопасным. Причина может быть легко объяснена.
Проблема лежит в нижней строке кода
"is_terminate = true;"
Даже если доступ к "is_terminate" является атомарным, оператор выше не является атомарным.
Этот оператор содержит более 1 операции. Например, загрузите "is_terminate" и обновите "is_terminate" .
Теперь getcha - это если is_terminate загружен и не обновлен, а поток переключается на другой.
Теперь ожидаемый результат потока 2 будет правдой, но он не получит его.
Убедитесь, что значение is_terminate = true; является атомарным. Поэтому заблокируйте его.
Надеюсь, что это поможет.