Является ли С++ std:: set потокобезопасным?
У меня вопрос о безопасности потока std:: set.
Насколько я знаю, я могу перебирать элементы набора и добавления/стирания, и это не отменяет итераторов.
Но рассмотрим следующий сценарий:
- thread 'A' выполняет итерацию по набору shared_ptr <Type>
- поток 'B' иногда добавляет элементы к этому набору.
Я испытал segfaults по мере запуска программы, и я не уверен, почему это происходит. Отсутствует ли проблема безопасности потока?
Ответы
Ответ 1
STL не поддерживает встроенную поддержку потоков, поэтому вам нужно расширить STL
кода с вашими собственными механизмами синхронизации для использования STL в
многопоточная среда.
Например, посмотрите здесь: текст ссылки
Поскольку set - это контейнерный класс, MSDN имеет следующие значения для безопасности потоков контейнеров.
Один объект является потокобезопасным для чтения из нескольких потоков. Например, при заданном объекте A безопасно читать A из потока 1 и из потока 2 одновременно.
Если один объект записывается одним потоком, все записи и записи этого объекта на одном и том же или другом потоке должны быть защищены. Например, если задан объект A, если поток 1 записывает в A, то поток 2 должен быть запрещен для чтения или записи в A.
Безопасно читать и записывать один экземпляр типа, даже если другой поток читает или записывает в другой экземпляр того же типа. Например, для заданных объектов A и B того же типа, безопасно, если A записывается в потоке 1, а B читается в потоке 2.
Ответ 2
Документация STL-Dinkumware содержит следующий абзац об этой теме. Его вероятно (как указано в тексте), действительный для большинства реализаций.
Для объектов контейнера, определенных в стандартная библиотека С++, такая как STL Контейнеры и объекты шаблона class basic_string, это осуществление следует широко принятые практики, изложенные для SGI STL:
Несколько потоков могут безопасно читать один и тот же объект-контейнер. (Есть nunprotected измененные подобъекты внутри контейнерный объект.)
Два потока могут безопасно манипулировать различными контейнерными объектами одного типа. (Нет незащищенные общие статические объекты в пределах типа контейнера.)
Вы должны защищать от одновременного доступа к контейнеру объект, если хотя бы один поток изменение объекта. (Очевидное примитивы синхронизации, такие как те в библиотеке нитей Dinkum, не будет разрушаться контейнером объект.)
Таким образом, не предпринимаются попытки обеспечить что атомные операции на контейнере объекты являются потокобезопасными; но это достаточно легко сделать общий контейнер объекты, которые являются потокобезопасными на соответствующий уровень детализации.
Ответ 3
Ни один из контейнеров STL не является потокобезопасным, поэтому std::set
в частности isnt.
В вашем случае проблема даже не связана с потоковой безопасностью: вы просто обмениваетесь объектом через несколько потоков (отлично) и изменяете его в одном потоке (отлично также). Но, как вы уже сказали, изменение контейнера делает недействительными его итераторы. Независимо от того, происходит ли это в одном и том же потоке или в другом потоке, это не имеет никакого значения, поскольку его еще один и тот же контейнер.
D'ах! В разделе 23.1.2.8 указано, что вставка не делает недействительными итераторы.
Ответ 4
Простое объяснение: если поток A перемещает итераторы через контейнер, он смотрит на внутренности контейнера. Если поток B изменяет контейнер (даже операция, которая не делает недействительным итератор, который имеет A), поток A может столкнуться с проблемами, потому что B обманывает внутренности контейнера, возможно, имея их в (временно) недействительном состоянии. Это приводит к сбоям в потоке A.
Проблема НЕ является самими итераторами. Это когда им нужны структуры данных контейнера, чтобы найти положение, в котором вы попадаете.
Просто как это.
Ответ 5
Да. Один из способов справиться с этой ситуацией состоит в том, чтобы каждый поток блокировал общий мьютекс перед доступом к одному и тому же заданному объекту. Убедитесь, что вы используете методы RAII для блокировки и разблокировки мьютекса.
Ответ 6
Выполнение вставки может привести к тому, что вектор перераспределит свою базовую память, в то время как итератор все еще может указывать на предыдущий (но недействительный) адрес памяти, что приводит к сбою сегмента.