Что значит сделать динамический массив потокобезопасным и параллельным? Скажите, например, std::vector.

  1. Два потока могут захотеть вставить в одну и ту же позицию. Синхронизация не требуется, так как она будет выполняться в соответствии с расписанием потоков.
  2. Один поток стирается, а другой собирается получить доступ к одному и тому же элементу? Я считаю, что это не проблема структуры данных, это проблема использования.

Итак, нужно ли что-нибудь сделать над std::vector, чтобы сделать его потокобезопасным и параллельным, или он по умолчанию является потокобезопасным и параллельным?

12
Abhishek Jain 30 Июн 2015 в 10:07
2
Если явно не указано иное, считайте все потоком небезопасным, тогда ваши предположения будут ошибаться в безопасности. И оба ваших примера нуждаются в какой-то синхронизации или взаимном исключении, помните, что поток может быть вытеснен в любое время.
 – 
Some programmer dude
30 Июн 2015 в 10:09
5
Единственное, что вы можете надежно делать одновременно со стандартными контейнерами (из которых std::vector - один), - это чтение . Как только вы вводите потенциал для одновременной операции записи на одном и том же объекте внутри, колеса начинают падать, и если сами объекты не поддерживают атомарные обновления в этом сценарии, колеса покинули транспортное средство. . И если сам контейнер подвергнется какой-либо механике, потенциально изменяющей компоновку, колеса уже будут в четверти мили позади вас в канаве.
 – 
WhozCraig
30 Июн 2015 в 10:15
Контейнеры STL не являются потокобезопасными в вашем сценарии. Вам придется либо использовать блокировку для сериализации доступа, либо использовать разные контейнеры, которые являются потокобезопасными.
 – 
Panagiotis Kanavos
30 Июн 2015 в 10:27
Попробуйте контейнеры LockFree Boost.
 – 
Panagiotis Kanavos
30 Июн 2015 в 10:34
2
@PanagiotisKanavos: при создании ссылки на boost.org замените номер версии в URL-адресе на "release". Таким образом, ваша ссылка не устареет.
 – 
DevSolar
30 Июн 2015 в 10:44

2 ответа

Лучший ответ

C ++ 11 говорит следующее о потокобезопасности контейнеров в стандартной библиотеке:

23.2.2 Гонки данных контейнеров [container.requirements.dataraces]

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

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

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

Итак, ни один из ваших двух конкретных вопросов не является потокобезопасным для std::vector:

1) Два потока, вставляемые в вектор, изменяют сам вектор, а не существующие отдельные элементы.

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

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

23
Michael Burr 30 Июн 2015 в 18:09
Ясно как грязь Михаил: с.
 – 
Robinson
30 Июн 2015 в 10:24
2
Я думаю, что подразумевается важная часть: добавление или удаление не потокобезопасно. Этот ответ следует изменить, чтобы указать, что любая другая операция, кроме описанных, не безопасна для trhead.
 – 
Panagiotis Kanavos
30 Июн 2015 в 10:24
Хотя перечисленные операции могут быть потокобезопасными, они не могут выполняться одновременно. Подумайте, например, о сценарии, когда один поток использует оператор индекса [] в векторе, чтобы получить элемент с индексом X, поток прерывается сразу после начала вызова, а другой поток вставляет новый элемент. перед индексом X. Тогда начальный поток все равно получит элемент с индексом X, но это могут быть не те данные, которые ожидал поток.
 – 
Some programmer dude
30 Июн 2015 в 10:32
2
@JoachimPileborg: операция вставки не указана как «безопасная для гонки данных». Итак, 23.2.2 не предлагает ситуации, которую вы описываете как безопасную.
 – 
Michael Burr
30 Июн 2015 в 18:09
Одновременное изменение одного и того же элемента разрешено если элементы индивидуальны atomic. Но обратите внимание, что atomic<T> не имеет конструктора копирования, поэтому невозможно использовать какие-либо функции, которые проверяют, должны ли они увеличивать вектор, независимо от шагов, которые вы предпринимаете, чтобы убедиться, что он не нужен расти. Код из неиспользованных веток по-прежнему требуется для компиляции на C ++. Как объявить атомарный вектор в C ++) . Это по-прежнему не делает безопасным изменение самого контейнера, а только его элементы.
 – 
Peter Cordes
4 Дек 2017 в 11:44

Единственные параллельные операции над одним объектом в стандартной библиотеке, которые по умолчанию безопасны: - Доступ только к const - функциям-членам - Все обращения к примитивам синхронизации (например, блокировка и разблокировка мьютекса или атомарные операции) Все остальное необходимо синхронизировать извне. В частности, в стандартной библиотеке пока нет потоковобезопасных контейнеров (начиная с C ++ 14).

Итак, ответ на оба ваших примера - нет, они оба требуют внешней синхронизации.

Что вы, конечно, можете сделать, так это изменить значение двух разных элементов в контейнере.

2
MikeMB 29 Июл 2015 в 10:28