Нужно ли загружать и хранить std :: atomi c? - PullRequest
2 голосов
/ 26 февраля 2020

Согласно этой статье :

Каждый раз, когда два потока работают с общей переменной одновременно, и одна из этих операций выполняет запись, оба потока должны использовать atomi c операции.

Однако, если потоком с более низким приоритетом является записывающее устройство, а потоком с более высоким приоритетом является считывающее устройство, должен ли поток с более низким приоритетом принудительно хранить хранилища atomi c? Мне кажется, что только поток с более высоким приоритетом должен обеспечивать атомную нагрузку c:

#include <atomic>

std::atomic<T*> ptr; // assume initialized to some non-null value

void highPriThreadFcn(void)
{
    T* local_ptr = ptr.load(); // need atomic load here in case lowPriThread write/store was interrupted
}

void lowPriThreadFcn(T* some_ptr)
{
    ptr = some_ptr; // do I need an atomic store here? I'd think not, as ptr is not written to by highPriThread
}

Аналогичный вопрос будет применяться в «обратном» случае (запись в поток с высоким приоритетом, чтение из ветки с низким приоритетом):

void highPriThreadFcn(T* some_ptr)
{
    ptr = some_ptr; // do I need an atomic store here? I'd think not, as write to ptr cannot be interrupted
}

void lowPriThreadFcn(void)
{
    T* local_ptr = ptr.load(); // need atomic load here in case read/load was interrupted
}

Ответы [ 2 ]

3 голосов
/ 26 февраля 2020

Нельзя хранить или загружать неатоми c или загружать переменную atomi c. API гарантирует, что нет никакого способа сделать это. Ваш якобы "non-atomi c store",

ptr = some_ptr;

на самом деле представляет собой atomi c store с последовательным последовательным упорядочением. См. atomi c :: operator = on cppreference .

Кстати, если вы подумали об изменении переменной atomi c на переменную не Atomi c на котором только некоторые операции выполняются атомарно: не делайте этого. Всякий раз, когда загрузка и хранение в одной и той же ячейке памяти являются «потенциально одновременными», стандарт требует, чтобы они оба были атомами c. В противном случае поведение не определено. Это означает, что компилятору разрешено «оптимизировать» таким образом, который нарушает ваш код. То, имеет ли поток «более высокий» приоритет, чем другой, не влияет на это.

0 голосов
/ 26 февраля 2020

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

В качестве примера ситуации, когда вещи могут go ошибаться, рассмотрим что-то вроде:

extern int foo;

int x=foo;
.... some code that modifies neither x nor foo
int y=x;
.... some code that modifies neither y nor foo (but might modify x)
doSomething(x, y);

Было бы вполне правдоподобно, что оптимизирующий компилятор мог бы заменить последний вызов функции с doSomething(x, foo) при условии, что foo должно равняться значению, которое y имел бы во время первоначального присваивания. Вместо того, чтобы использовать модель абстракции, которая допускала бы ограниченный недетерминизм, авторы Стандарта просто отказались предоставить какие-либо поведенческие гарантии в отношении последствий неожиданного изменения значений объектов.

...