Выдержка из документации от emplace_back()
:
- Срок действия итератора
Все итераторы, связанные с этим контейнером, становятся недействительными, но указатели и ссылки остаются действительными, ссылаясь на те же элементы, на которые они ссылались перед вызовом.
- Гонки данных
Контейнер изменен.
При вызове нет доступа к содержащимся элементам: одновременный доступ к ним или их изменение безопасны (хотя см. валидность итератора выше).
И выдержка из документации от operator[]()
:
- Гонки данных
Доступ к контейнеру (ни const, ни неконстантные версии не изменяют контейнер).
Элемент N потенциально доступен или изменен. Одновременный доступ к другим элементам или их изменение безопасны.
Итак, учитывая, что какой-то случай Deque имеет хотя бы один элемент, доступ к которому осуществляется через operator[]()
и звонит emplace_back()
на контейнере действительно ли потокобезопасен?
Я склонен сказать, что это так, но не могу решить, если «доступ» в emplace_back()
Документы включают в себя использование operator[]()
как в:
int access( std::deque< int > & q )
{
return q[ 0 ];
}
void emplace( std::deque< int > & q , int i )
{
q.emplace_back( i );
}
где обе функции вызываются одновременно или этот «доступ» применяется только к элементам, некоторые ссылки или указатели которых уже были взяты:
std::deque< int > q { 1 };
auto * ptr = & q[ 0 ]
std::thread t1 ( [ ptr ]{ * ref = 0; } );
std::thread t2 ( [ & q ]{ q.emplace_back( 2 ); } );
редактировать: Для дальнейшего ознакомления, вот что стандарт C ++ 14 (на самом деле, Ноябрь 2014 Рабочий проект, N4296) говорится о вставках в deque
по отношению к ссылкам и действительности итераторов:
- 23.3.3.4 модификаторы deque
(…)
- Эффекты: вставка в середине deque делает недействительными все итераторы и ссылки на элементы deque. Вставка в любом конце deque делает недействительными все итераторы в deque, но не влияет на достоверность ссылок на элементы deque.
(…)
Вызов любых двух методов для объекта стандартного класса одновременно небезопасен, если оба const
или если не указано иное (например, как в случае с std::mutex::lock()
). Это изучено более подробно Вот
Итак, используя emplace_back
а также operator[]
одновременно не безопасный. Тем не менее, вы можете смело использовать ранее полученную ссылку на deque
элемент одновременно с вызовом emplace_back
/push_back
из-за указанных вами правил действия ссылки / указателя, например:
int main()
{
std::deque<int> d;
d.push_back(5);
auto &first = d[0];
auto task = std::async(std::launch::async, [&] { first=3; });
d.push_back(7);
task.wait();
for ( auto i : d )
std::cout << i << '\n';
}
Это будет безопасно выводить 3 и 7. Обратите внимание, что ссылка first
создается перед запуском асинхронной задачи.
Редактировать примечание: этот ответ неверен в том, что []
а также emplace_back
безопасно использовать одновременно. Арне ответ правильный. Оставляя это здесь, а не удаляя из-за полезных комментариев.
Edit2: Ну, технически я не сделал такой вывод, но это как бы подразумевается. Арне лучший ответ.
То, что эта документация говорит, хотя я не совсем доверяю источнику, это то, что одновременный доступ Другой значения являются потокобезопасными, если вы не делаете это через итератор.
Причина этого в том, что emplace_back
не будет касаться других значений в любом случае. Если емкость слишком мала для добавления другого элемента, выделяется новая страница. Это не влияет на другие элементы. Так что безопасно использовать эти значения через другие потоки. Это никогда не вызовет гонки данных, потому что те же данные не доступны и не изменяются.
Контейнер не должен быть каким-либо образом безопасным для потоков, чтобы это имело место. Это похоже на доступ a[0]
пока вы модифицируете a[1]
, До тех пор, пока вы правильно обращаетесь к этим данным (не вызываете UB), это безопасная операция. Вам не нужны никакие блокировки для защиты, потому что вы не используете одновременно одни и те же данные.
Что меня больше беспокоит, так это size
, Это, вероятно, будет значение в deque
который изменен emplace
и читать size
, Это может привести к гонке данных, если нет защиты. Документация ничего не говорит об этом и касается только доступа к элементам, что, конечно, нормально делать одновременно с вызовом size
,
В соответствии с этот ответ Стандартные контейнеры не имеют никаких гарантий относительно безопасности резьбы, за исключением вышеперечисленного. Другими словами, вы можете одновременно получить доступ / изменить разные элементы, но все остальное может вызвать гонку данных. Иными словами, стандартные контейнеры не являются потокобезопасными и не обеспечивают никакой защиты от параллелизма.