2014-11-19 5 views
1
void foo(volatile int& a, volatile int& b, bool threadOne) { 
    if (threadOne) { 
     //EDIT: switched the following two lines 
     b = 10; 
     a = 5; 
    } else { 
     while(a == 0); 
     cout << a << b; 
    } 
} 

//somewhere else 
int a = 0; 
int b = 0; 
std::thread th1(foo, a, b, true); 
std::thread th2(foo, a, b, false); 

Это безопасно на x86, то есть каждый законный чередование инструкций в соответствии со стандартом C++ и ссылкой x86 будет печатать «510»? Моя гипотеза заключается в том, что она на самом деле безопасна.Безопасно ли защищать переменную с другой переменной?

+3

Я не думаю, что слово «безопасно» на самом деле означает что-либо, и вы уверены, что не определяли его, поэтому -1 для этого. Ваш код имеет неопределенное поведение в отношении C++. –

+3

«Является ли это безопасным на x86, то есть каждый законный чередование инструкций в соответствии со стандартом C++ и ссылкой x86 будет печатать« 510 »?» Разве это не определение? – ben

+1

@KerrekSB: Безопасно определенно [делает] (http://en.wikipedia.org/wiki/Thread_safety) означает что-то в многопоточном контексте, поэтому вопрос помечен как «многопоточность». – wolfPack88

ответ

4

tl; dr Нет, нет, нет, в тысячу раз нет.

Что касается стандарта C++ 11, то программа имеет неопределенное поведение, поскольку она имеет гонку данных (a считывается и записывается параллельными исполнением без синхронизации). Так что нет, он не всегда будет печатать 510. volatile не имеет никакого эффекта на этом, это не значит, что volatile в C# и Java означает.

Я также подозреваю (но не могу с полной уверенностью утверждать), что, как правило, чип x86 может также нарушить эту программу, хотя, возможно, не из-за чередования. Тем не менее,

  1. Новое значение a может прийти в действие до того, как новое значение b становится видимым (поскольку ни забор памяти не предотвращает чип от переупорядочения операций записи).
  2. Новое значение любой переменной никогда не может стать видимым для второго потока. Проверьте протоколы согласованности кеша.

Вы играете с огнем. По какой-то причине для загрузки, переменные условия дешевы. Если ожидаемая задержка достаточно мала для переменной состояния до , то не будет дешевой, тогда вместо нее будет дешевой спин-блокировка (с правильной синхронизацией, т. Е. Атомика).

+0

Ваше подозрение относительно того, что никогда не становится видимым, неверно. ИМХО: http://en.wikipedia.org/wiki/MESI_protocol – ben

+1

@ben: Собственно, он совершенно прав. Компилятор может выбрать сохранение 'a' в регистре, и в этом случае протоколы когерентности кэша не применяются. –

+0

@JerryCoffin. Определятель 'volatile' может предотвратить это (с предупреждением, что стандарт не знает или не заботится о регистрах, поэтому этот эффект будет специфичным для реализации). – delnan

5

§1.10 [intro.multithread] (со ссылкой на N4140):

6 Два оценки экспрессии конфликт если один из них изменяет местоположение памяти (1.7), а другой получает доступ или изменяет то же самое память место.

23 Два действия являются потенциально одновременно если

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

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

Один из ваших потоков не читает a, другой пишет в a, ни атомная, и ни один из этих двух потенциально конфликтующих одновременных действий происходит до другого. Следовательно, у вас есть гонка данных и неопределенное поведение.

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