Volatile и многопоточность: является ли следующий потокобезопасным? - PullRequest
22 голосов
/ 06 июля 2011

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

volatile bool is_terminate = false;

void Thread1()
{
    is_terminate = true;
}

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

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

Ответы [ 9 ]

17 голосов
/ 06 июля 2011

Это , вероятно, своего рода потокобезопасный.

Безопасность потоков обычно зависит от контекста. Обновление bool является всегда потокобезопасным, если вы никогда не читаете из него. И если вы читаете с него, то ответ зависит от , когда вы читаете с него, и от того, что означает это чтение.

На некоторых процессорах, но не на всех, запись в объект типа bool будет атомарной. Процессоры x86 обычно делают его атомарным, а другие нет. Если обновление не является атомарным, добавление volatile вам не поможет.

Но следующая проблема - это переупорядочение. Компилятор (и ЦП) будут выполнять чтение / запись переменных volatile в указанном порядке, без переупорядочения. Так что это хорошо.

Но это не гарантирует переупорядочения одного доступа к памяти volatile относительно всех не volatile. Таким распространенным примером является то, что вы определяете какой-то флаг для защиты доступа к ресурсу, вы делаете флаг volatile, а затем компилятор перемещает доступ к ресурсу вверх, так что это происходит за до , когда вы проверяете флаг , Это разрешено делать, потому что это не переупорядочение внутреннего порядка двух volatile обращений, а просто volatile и не-1027 * одного.

Честно говоря, я задам вопрос почему бы просто не сделать это правильно ? Возможно, что volatile будет работать в этой ситуации, но почему бы не избавить себя от проблем и не прояснить, что это правильно? Вместо этого наложите на него барьер памяти.

11 голосов
/ 06 июля 2011

Это не потокобезопасно.

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


Чтобы ответить по-другому:

Если volatile достаточно для обеспечения безопасности потока, почемуC ++ 0x добавляет целую главу с атомарными операциями?

http://www.open -std.org / jtc1 / sc22 / wg21 / docs / paper / 2006 / n2047.html

4 голосов
/ 06 июля 2011

Во-первых, volatile используется для отключения оптимизации компиляции в c / c ++. см. это для понимания изменчивости.

Ядром атомарного элемента является выравнивание слов и размер is_terminate , если размер is_terminate меньше собственного размера машины и выровнен, то R и W этого числа атомарные.

В вашем контексте , с или без volatile, thread2 может читать старое значение после того, как thread1 изменило его, но thread2 может прочитать его в конце концов.

Если в конечном итоге чтение для вас нормально, тогда ваши коды безопасны для потоков.

2 голосов
/ 06 июля 2011

это безопасно, потому что один поток только читает, а другой только пишет.

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

1 голос
/ 06 июля 2011

Большая проблема с предположением, что ключевое слово volatile налагает любой вид безопасности потока, состоит в том, что стандарты C или C ++ не имеют понятия потоков в абстрактной машине, которую они описывают.

Гарантии, что стандарт налагает на ключевое слово volatile, действительны только в пределах потока - не между несколькими потоками.

Это дает разработчикам полную свободу делать все, что им угодно, когда дело доходит до потоков. Если они решили внедрить ключевое слово volatile для обеспечения безопасности в потоках, то вам повезло. Чаще всего это не так.

1 голос
/ 06 июля 2011

Я считаю, что этот код безопасен, пока оба потока не пишут bool (уже упоминалось, что значение доступа равно atomic ).

1 голос
/ 06 июля 2011

Это все еще не безопасно.Вы должны использовать синхронизацию для доступа к is_terminate Доступ к bool не гарантированно является атомарной операцией.

1 голос
/ 06 июля 2011

Нет, это не так.Это может быть потокобезопасным , если значение доступа было бы атомарным , но в C ++ нельзя предполагать, что доступ к переменным является потокобезопасным, если вы не используете некоторые специфичные для компилятора конструкции или примитивы синхронизации.

0 голосов
/ 06 июля 2011

Этот код не является потокобезопасным.Причина может быть объяснена легко.

Проблема заключается в строке кода ниже

"is_terminate = true;"

Даже если доступ к "is_terminate" является атомарным, приведенное выше утверждение не являетсяатомное.Это утверждение включает в себя более 1 операций.Как загрузить "is_terminate" и обновить "is_terminate".

Теперь получилось, если is_terminate загружен и не обновлен, а поток переключается на другой.Теперь поток 2 ожидал, что результат будет истинным, но он не получит его.

Убедитесь, что "is_terminate = true;"атомно.Так что запри это.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...