Почему это приобретает и освобождает память, не дает постоянную ценность? - PullRequest
1 голос
/ 31 мая 2019

Я просто изучаю использование захвата и освобождения памяти и не понимаю, почему иногда я получаю значение, равное нулю, а не значение 2 все время

Я запускал программу несколько раз и предполагал, что атомное хранилище до барьера освобождения и атомная нагрузка после барьера захвата гарантируют, что значения всегда будут синхронизироваться

#include <iostream>
#include <thread>
#include <atomic>

std::atomic<int>x;



void write()
{


    x.store(2,std::memory_order_relaxed);

    std::atomic_thread_fence(std::memory_order_release);



}

void read()
{

    std::atomic_thread_fence(std::memory_order_acquire);

    // THIS DOES NOT GIVE THE EXPECTED VALUE OF 2 SOMETIMES
    std::cout<<x.load(std::memory_order_relaxed)<<std::endl; 

}

int main()
{

std::thread t1(write);
std::thread t2(read);
t1.join();
t2.join();
return 0;
}

атомная переменная x иногда дает значение 0

Ответы [ 3 ]

2 голосов
/ 31 мая 2019

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

В некоторых публикациях будет описано, что операция освобождения в одном потоке «синхронизируется» с последующей операцией получения в другом потоке. Ключом к этому является то, что действие по приобретению является последующим действием (то есть, приобретение приобретается «после» выпуска). Если действие освобождения упорядочено после действия получения, то между операциями записи и чтения не возникает никакого отношения к синхронизации.

Причина, по которой ваш код не всегда возвращает ожидаемое, состоит в том, что чередование потоков иногда упорядочивает запись перед чтением, иногда чтение перед записью.

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

Я рекомендую вам прочитать очень хорошо написанное сообщение в блоге о выпуске и овладеть семантикой и отношением "синхронизирует с" в Preshing при программировании Отношение "Синхронизируется с" .

1 голос
/ 31 мая 2019

Похоже, вы неправильно использовали забор.Вы пытаетесь использовать его как мьютекс, верно?Если вы ожидаете, что код всегда будет выводить 2, вы просто думаете, что операция load никогда не будет выполнена до операции save.Но это не то, что делает забор памяти, это то, что делают примитивы синхронизации.

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

0 голосов
/ 31 мая 2019

Причины просты: ваши заборы ничего не делают и в любом случае не могут здесь быть полезными, потому что нет записи, что забор сделал бы видимым (на стороне выпуска) сторону получения .

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

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

ДОПОЛНИТЕЛЬНОЕ ОБЪЯСНЕНИЕ

Способ объяснить семантику rel / ack таков:

  • release означает « Я достиг чего-то », и яустановите для этого атомарного объекта какое-то значение, чтобы опубликовать это утверждение;
  • приобретает означает " вы что-то сделали? ", и я прочитал этот атомарный объект, чтобы увидеть, содержит ли онм.

Поэтому выпуск до того, как вы что-то сделали, не имеет смысла, а приобретение, которое выбрасывает информацию, содержащую претензию, как в (void)x.load(memory_order_acquire), как правило, бессмысленно, так как нет знаний (в целом) очто было приобретено, то есть о том, что было достигнуто.(Исключением из этого правила является ситуация, когда у потока были ослабленные загрузки или операции RMW.)

...