Является ли запись std :: deque в разных местах памяти одновременно поточно-ориентированной? - PullRequest
0 голосов
/ 31 мая 2018

У меня есть std::deque<std::pair<CustomObj, int>>, размер которого не изменяется при запуске параллельного блока.

Параллельный блок читает каждый CustomObj из deque и устанавливает int.

Я могу гарантировать, что deque не изменит размер, следовательно, он не будет перераспределен, и что каждый поток получит доступ только к фрагменту памяти deque, но не к другому потоку.

Ведет ли онк неопределенному поведению чтения и записи одновременно?Должен ли я поместить письмо и чтение в зону взаимного исключения?

Ответы [ 3 ]

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

Arule для всех стандартных контейнеров:

  • Множество считывателей или один писатель на весь контейнер и на каждый элемент.
  • Чтение или изменение отдельных элементов(без добавления / удаления элемента) также является операцией чтения в контейнере.

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


В стандарте это обычно формулируется в терминах методов const для контейнера.Метод чтения - const, метод записи - const.Исключением является то, что begin() и end() и data() (методы, которые просто возвращают итераторы), не являющиеся const, считаются const.

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

В качестве примера случая, когда эмпирическое правило выше говорит «нет», а стандарт говорит:"ok":

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

Поскольку никакие итераторы не аннулированы картой, и вы не касаетесь ключа, я полагаю, что нет условий гонки.

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

Удивительно, но на самом деле в самом текущем стандарте есть очень четкий раздел об этом:

(C ++ 17, 26.2.2 Гонки контейнерных данных, 2)

Несмотря на 20.5.5.9, реализации должны избегать гонок данных, когда содержимое содержимого объекта в различных элементах одного и того же контейнера, за исключением vector<bool>, изменяется одновременно.

Также вам разрешается вызывать следующие методы доступа без беспокойства:

  1. Во избежание скачек данных (20.5.5.9) реализации должны считать следующие функции постоянными: begin, end, rbegin, rend, front, back, data, find, lower_bound, upper_bound, equal_range, atи, за исключением ассоциативных или неупорядоченных ассоциативных контейнеров, operator[].

Поскольку std::deque не является исключением, вы можете одновременно вызывать любую из этих функций для получения различных элементов и измененияих.Просто убедитесь, что правильно изолировали любые модификации самого контейнера от параллельной области, где вы одновременно обращаетесь и изменяете элементы.

Например, это будет неправильно:

std::dequeue<...> dq;
#pragma omp master
{
    ...
    dq.emplace(...);
}
// no implicit barrier here,
// use omp barrier or change to omp single instead of master
#pragma omp for
for (... i; ...)
    dq[i].second = compute(dq[i]);
0 голосов
/ 31 мая 2018

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

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

...