Я не уверен в том, как гарантии упорядочения памяти атомарных переменных в c ++ 11 влияют на операции с другой памятью.
Допустим, у меня есть один поток, который периодически вызывает функцию записи для обновления значения, и другой поток, который вызывает чтение для получения текущего значения. Гарантируется ли, что последствия d = value;
не будет видно до воздействия a = version;
и будет видно до воздействия b = version;
?
atomic<int> a {0};
atomic<int> b {0};
double d;
void write(int version, double value) {
a = version;
d = value;
b = version;
}
double read() {
int x,y;
double ret;
do {
x = b;
ret = d;
y = a;
} while (x != y);
return ret;
}
Гарантируется ли, что последствия
d = value;
не будет видно до воздействияa = version;
и будет видно до воздействияb = version;
?
Да, это. Это потому что последовательный барьер согласованности подразумевается при чтении или записи atomic<>
переменная.
Вместо хранения version
тег в два атомарные переменные до изменения значения и после него, вы можете увеличивать не замужем атомная переменная до и после модификации:
atomic<int> a = {0};
double d;
void write(double value)
{
a = a + 1; // 'a' become odd
d = value; //or other modification of protected value(s)
a = a + 1; // 'a' become even, but not equal to the one before modification
}
double read(void)
{
int x;
double ret;
do
{
x = a;
ret = value; // or other action with protected value(s)
} while((x & 2) || (x != a));
return ret;
}
Это известно как seqlock в ядре Linux: http://en.wikipedia.org/wiki/Seqlock
Правило таково, что, учитывая write
поток, который выполняется один раз, и ничего больше, что изменяет a
, b
или же d
,
a
а также b
из другого потока в любое время, иb
и посмотреть version
хранится в нем, то
d
; а такжеvalue
,Обратите внимание, что истинность второй части зависит от порядка в памяти; это верно по умолчанию (memory_order_seq_cst
).
Ваш объект d
записывается и читается двумя потоками и не является атомарным. Это небезопасно, как предлагается в стандарте C ++ по многопоточности:
1.10 / 4 Две оценки выражений конфликтуют, если одна из них изменяет ячейку памяти, а другая обращается или изменяет ту же ячейку памяти.
1.10 / 21 Выполнение программы содержит гонку данных, если она содержит два конфликтующих действия в разных потоках, по крайней мере, один из
который не является атомным, и ни один не происходит раньше другого. Любое такое
гонка данных приводит к неопределенному поведению.
Важное редактирование:
В вашем неатомарном случае у вас нет никаких гарантий относительно порядка чтения и записи. У вас даже нет гарантии, что читатель прочитает значение, которое было написано автором (это короткая статья объясняет риск для неатомарных переменных).
тем не менее, цикл вашего читателя заканчивается на основе проверки окружающих атомных переменных, для которых есть строгие гарантии. При условии, что version
никогда не повторяется между разными вызовами писателя и в обратном порядке, в котором вы приобретаете их значение:
d
читать по сравнению с d
Писать не может быть неудачным, если два атома равны. Это означает, что в случае неблагоприятного состояния гонки на вашем неатомном, благодаря петле, вы в конечном итоге прочитаете последний value
,