Подтвердить что ты не робот

Неустойчивость и многопоточность: является ли поточно-безопасным?

Предположим, что существуют два потока, выполняющих Thread1() и Thread2() соответственно. Нить 1 просто устанавливает глобальный флаг, чтобы сообщить, что поток 2 прекратит работу, а поток 2 периодически проверяет, должен ли он выйти.

volatile bool is_terminate = false;

void Thread1()
{
    is_terminate = true;
}

void Thread2()
{
    while (!is_terminate) {
        // ...
    }
}

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

4b9b3361

Ответ 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; является атомарным. Поэтому заблокируйте его.

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