Является ли C ++ std :: set поточно-ориентированным? - PullRequest
36 голосов
/ 01 сентября 2009

У меня есть вопрос о безопасности потоков std :: set.

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

Но рассмотрим следующий сценарий:

  • поток 'A' перебирает набор shared_ptr
  • тема 'B' иногда добавляет элементы в этот набор.

Я испытал segfaults во время работы программы, и я не уверен, почему это происходит. Является ли отсутствие безопасности нитей причиной?

Ответы [ 6 ]

29 голосов
/ 01 сентября 2009

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

Например, посмотрите здесь: текст ссылки

Поскольку set - это контейнерный класс, MSDN должен сказать о безопасности потоков в контейнерах.

Один объект является поточно-ориентированным для чтения из нескольких потоков. Например, для объекта A безопасно считывать A из потока 1 и из потока 2 одновременно.

Если один объект записывается одним потоком, то все операции чтения и записи в этот объект в том же или других потоках должны быть защищены. Например, если для объекта A задан поток 1, записывающий в A, то поток 2 должен быть запрещен для чтения или записи в A.

Безопасно читать и записывать в один экземпляр типа, даже если другой поток читает или записывает в другой экземпляр того же типа. Например, для объектов A и B одного типа безопасно, если A записывается в потоке 1, а B читается в потоке 2.

23 голосов
/ 01 сентября 2009

Документация Dinkumware STL содержит следующий параграф по этой теме. Вероятно, (как указано в тексте) действительно для большинства реализаций.

Для объектов-контейнеров, определенных в Стандартная библиотека C ++, такая как STL Контейнеры и объекты шаблона класс basic_string, это реализация следует широко принятая практика изложена для SGI STL:

Несколько потоков могут безопасно читать один и тот же контейнерный объект. (Есть незащищенные изменяемые подобъекты в контейнерный объект.)

Два потока могут безопасно управлять различными объектами контейнера того же типа. (Нет незащищенные общие статические объекты внутри типа контейнера.)

Вы должны защитить от одновременного доступа к контейнеру объект, если хотя бы один поток Модификация объекта. (Очевидное примитивы синхронизации, такие как те, в библиотеке потоков Dinkum, не будет подорван контейнером объект.)

Таким образом, не делается никаких попыток обеспечить что атомарные операции над контейнером объекты потокобезопасны; но это достаточно просто сделать общий контейнер объекты, которые потокобезопасны на соответствующий уровень детализации.

10 голосов
/ 01 сентября 2009

Ни один из контейнеров STL не является поточно-ориентированным, поэтому std::set, в частности, не является.

В вашем случае проблема даже не в безопасности потоков, хотя: вы просто разделяете объект между несколькими потоками (хорошо) и изменяете его в одном потоке (также хорошо). Но, как вы уже сказали, изменение контейнера делает его итераторы недействительными. Происходит ли это в том же потоке или в другом потоке, это не имеет значения, поскольку это все еще тот же контейнер .

D'Oh! §23.1.2.8 утверждает, что вставка не делает недействительными итераторы.

2 голосов
/ 01 сентября 2009

Простое объяснение: если поток A перемещает итераторы по контейнеру, он смотрит на внутренние компоненты контейнера. Если поток B изменяет контейнер (даже операция, которая не делает недействительным итератор, который есть у A), поток A может столкнуться с проблемами, потому что B дурачит внутренние компоненты контейнера, возможно, имея их (временно) в недопустимом состоянии. Это вызывает сбои в потоке A.

Проблема не в самих итераторах. Это когда им нужны структуры данных контейнера, чтобы найти положение, в которое вы попадаете.

Просто так.

1 голос
/ 18 января 2013

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

1 голос
/ 01 сентября 2009

Да. Один из способов справиться с этой ситуацией состоит в том, чтобы каждый поток блокировал общий мьютекс перед доступом к одному и тому же набору объектов. Убедитесь, что вы используете методы RAII для блокировки и разблокировки мьютекса.

...