Я пытаюсь понять порядок памяти, вызванный atomic_exchange[_explicit]
, и написал следующий код:
static _Atomic int i = 123;
void *update(void *ignored){
if(atomic_exchange_explicit(&i, 200, memory_order_release) != 200){ //1
printf("Updated\n", stdout);
}
}
int main(int args, const char *argv[]){
pthread_t t1;
pthread_create(&t1, NULL, &update, NULL);
pthread_t t2;
pthread_create(&t2, NULL, &update, NULL);
sleep(1000);
}
Вопрос : Возможно ли, что Updated\n
будет напечатан дважды (обеими нитями)?
Я думаю поведение не определено . UB удерживается, даже если мы заменим memory_order_release
на memory_order_acquire
в //1
. Сгенерированный код такой же для acq / rel / acq_rel: https://godbolt.org/z/sFjcve.
Это потому, что у нас гонка данных, так как для синхронизации с отношением, которое необходимо удерживать, нам нужна одна операция - операция acquire
, а следующая операция - операция release
, а операция освобождения считывает значение, записанное побочным эффектом. из последовательности разблокирования операции получения 5.1.2.4(p11)
:
В частности, атомарная операция A, которая выполняет операцию освобождения
на объекте M синхронизируется с атомарной операцией B, которая выполняет
операция получения на M и читает значение, записанное любым побочным эффектом
в последовательности выпуска, возглавляемой А.
Таким образом, единственный способ сделать поведение этого кода четким - заменить atomic_exchange_explicit(&i, 200, memory_order_release) != 200
на atomic_exchange(&i, 200) != 200