Boost Shared Pointer: одновременный доступ для чтения в нескольких потоках

У меня есть поток A, который выделяет память и назначает ее общему указателю. Затем этот поток порождает 3 других потока X, Y и Z и передает каждому копию общего указателя. Когда X, Y и Z выходят за рамки, память освобождается. Но есть ли вероятность того, что 2 потока X, Y выходят за рамки в один и тот же момент времени, и существует состояние гонки при подсчете ссылок, поэтому вместо уменьшения его на 2 он уменьшается только один раз. Итак, теперь счетчик ссылок newer падает до 0, поэтому происходит утечка памяти. Обратите внимание, что X, Y и Z только читают память. Не записывать или сбрасывать общий указатель. Короче говоря, может ли быть состояние гонки при подсчете ссылок и может ли это привести к утечке памяти?


person Nikhil    schedule 21.04.2010    source источник
comment
Интересно, что два человека использовали одну и ту же документацию, чтобы прийти к противоположным выводам.   -  person Mark Ransom    schedule 22.04.2010
comment
@Mark: я бы сказал, что документы не совсем кристально ясны (не сказать, что они неверны, просто их легко неправильно истолковать).   -  person Michael Burr    schedule 22.04.2010
comment
Я не понимаю, почему у вас есть награда за ваш вопрос. Ответ уже есть :)   -  person Vicente Botet Escriba    schedule 28.04.2010
comment
Надеюсь на более описательный ответ с фрагментом кода, возможно, просто небольшой стимул, всего 100 баллов.   -  person Nikhil    schedule 29.04.2010


Ответы (5)


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

Чтобы получить абсолютно неопровержимое доказательство, посмотрите, как Boost Smartptr реализует свои собственные мьютексы с нуля в boost/smart_ptr/detail/sp_counted_base_gcc_x86.hpp (или соответствующем файле вашей платформы).

person Potatoswatter    schedule 25.04.2010

boost::shared_ptr использует блокировки (или атомарный доступ без блокировок), чтобы обеспечить атомарное обновление счетчиков ссылок (даже если это неясно на странице документации). Вы можете отключить использование блокировок, если вы пишете однопоточный код, определив макрос BOOST_SP_DISABLE_THREADS.

Обратите внимание, что примеры документации на http://www.boost.org/doc/libs/1_42_0/libs/smart_ptr/shared_ptr.htm#ThreadSafety, в котором обсуждаются проблемы с множественной записью из разных потоков, обсуждает потоки, работающие с одними и теми же экземплярами shared_ptr (объекты shared_ptr могут быть глобальными в примеры), а не разные копии shared_ptr, которые указывают на один и тот же объект, что является обычным вариантом использования для shared_ptr. Пример, который вы приводите в вопросе (воздействие на копии, указывающие на общий объект), является потокобезопасным.

person Michael Burr    schedule 21.04.2010
comment
Комментарий совершенно правильный, но я бы также добавил, что вызов деструктора некоторых объектов из неправильного потока или в неподходящее время может привести к обычным видам многопоточных ошибок, таких как гонки и взаимоблокировки. Если уничтоженный объект освобождает только память, это не должно быть проблемой. Однако деструкторы, которые делают такие вещи, как отмена регистрации обреченного объекта в других частях системы, определенно могут вызвать проблемы. - person Doug; 01.05.2010
comment
@Doug, однако, это не имеет ничего общего с shared_ptr. Лучше думать с точки зрения заблокированного монопольного доступа, чем сбой из-за того, что он оказался не в том месте и не в то время. К каким бы ресурсам ни обращались деструкторы, они должны блокироваться. - person Potatoswatter; 02.05.2010
comment
@Potatoswatter: Да, я понимаю, что проблема, которую я поднял, не имеет прямого отношения к безопасности потоков shared_ptr. Я не думаю об условиях гонки, потому что она была не в том месте и не в то время. Я обнаружил ошибки, вызванные shared_ptrs, которые потеряли свою последнюю ссылку (скажем) в неосновном потоке и где деструктор выполняет нетривиальную очистку. Это точно так же, как удаление указателя вручную - такая же безопасность потока, как и необработанные указатели, как говорится в документации. - person Doug; 02.05.2010

Нет, согласно документации, эти проблемы не могут возникнуть:

Различные экземпляры shared_ptr могут быть "записаны" (доступны с помощью изменяемых операций, таких как operator= или reset) одновременно несколькими потоками (даже если эти экземпляры являются копиями и имеют один и тот же счетчик ссылок внизу).

person sth    schedule 21.04.2010
comment
Однако у меня есть вопрос: согласно документации, смешанная ситуация может привести к неопределенному поведению: (...) //--- Пример 4 --- // thread A p3 = p2; // читает p2, записывает p3 // поток B // p2 выходит за рамки: undefined, деструктор считается доступом для записи (...) разве мы не находимся в этой ситуации? - person Grimmy; 22.04.2010
comment
Пример ниже, который показывает поведение как «неопределенное», когда оно выходит за рамки. - person Brian Roach; 22.04.2010
comment
@Grimmy, @Brian: Насколько я понимаю, в примере 4 в документации один поток пытается прочитать переменную shared_ptr, находящуюся в другом потоке, в то время как эта переменная выходит за рамки. Это было бы неопределенным (и оно также было бы неопределенным для других типов переменных, а не только для shared_ptr). Если каждый поток получает свою собственную копию переменной shared_ptr, эти переменные могут использоваться (и выходить за рамки) независимо, даже если они указывают на один и тот же объект (как в примере 2 в документации). - person sth; 22.04.2010

В документации говорится:

Различные экземпляры shared_ptr могут быть «записаны» (доступны с помощью изменяемых операций, таких как operator= или reset) одновременно несколькими потоками (даже если эти экземпляры являются копиями и имеют один и тот же счетчик ссылок внизу).

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

person Philipp    schedule 21.04.2010

Лучше всего было бы перейти на TR1 или C++0x shared_ptr, а не на версию Boost. Я считаю, что стандартизировано, что они ДОЛЖНЫ быть потокобезопасными.

person Puppy    schedule 01.05.2010