Порядок памяти, введенный atomic_exchange_explicit / atomic_exchange

#c #multithreading #concurrency #stdatomic

#c #многопоточность #параллелизм #stdatomic

Вопрос:

Я пытаюсь понять порядок памяти, введенный atomic_exchange[_explicit] и написанный следующим кодом:

 static _Atomic int i = 123;

void *update(void *ignored){
    if(atomic_exchange_explicit(amp;i, 200, memory_order_release) != 200){ //1
        printf("Updatedn", stdout);
    } 
}

int main(int args, const char *argv[]){
    pthread_t t1;
    pthread_create(amp;t1, NULL, amp;update, NULL);

    pthread_t t2;
    pthread_create(amp;t2, NULL, amp;update, NULL);

    sleep(1000);
}
 

Вопрос: Возможно Updatedn ли, что он будет напечатан дважды (обоими потоками)?

Я думаю, что поведение не определено. UB сохраняется, даже если мы заменим memory_order_release на memory_order_acquire at //1 . Сгенерированный код тот же для acq / rel / acq_rel: https://godbolt.org/z/sFjcve .

Это связано с тем, что у нас есть гонка данных, поскольку для удержания синхронизации с отношением нам нужно, чтобы одна операция была acquire операцией, а следующая операция — release операцией, а операция освобождения считывает значение, записанное побочным эффектом, из последовательности освобождения операции получения 5.1.2.4(p11) :

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

Таким образом, единственный способ сделать поведение этого кода четко определенным — заменить atomic_exchange_explicit(amp;i, 200, memory_order_release) != 200 на atomic_exchange(amp;i, 200) != 200

Ответ №1:

Вы ошибаетесь. Независимо от запрошенного порядка памяти, обмен является атомарным. Порядок памяти объясняет, как эта операция чтения, записи или чтения-изменения-записи взаимодействует с другими операциями, но операция всегда атомарна.

Комментарии:

1. Я не заметил 7.17.3(p12) : атомарные операции чтения-изменения-записи всегда должны считывать последнее значение (в порядке изменения), сохраненное перед записью, связанной с операцией чтения-изменения-записи .. Так что, кажется, я вполне могу указать even memory_order_relaxed , и это не приведет к гонке данных, правильно?

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