Является ли boost :: interprocess :: shared_ptr поточно-безопасным (и межпроцессным)? - PullRequest
6 голосов
/ 20 марта 2011

Я хочу обмениваться данными между потоками и автоматически удалять их, когда последний пользователь закончил с ними.Кажется, это работает, в большинстве случаев, с использованием boost::interprocess::shared_ptr в boost::fixed_managed_shared_memory сегменте: но не всегда.

Итак, boost::interprocess::shared_ptr поточно (и межпроцессно) -безопасно?

Если я использую свою разделяемую память по фиксированному адресу (я почти уверен, что в моем 64-битном (ну, 48-битном) адресном пространстве все будет в порядке), можно ли использовать обычный * 1008?* (которые являются потокобезопасными) вместо этого?

некоторые пояснения:

Используемый мной тип указателя - обычный void* (моя общая память сопоставлена ​​с фиксированным адресом).

Вопрос о безопасности потоков связан с подсчетом ссылок, т. Е. Разрешено ли копирование / уничтожение общих указателей на одну и ту же вещь в разных процессах одновременно. Не доступ к одному и тому же общему указателю в разных потоках, а не доступ к pointee.

Ответы [ 6 ]

4 голосов
/ 28 апреля 2011

Счетчик ссылок, используемый в boost::interprocess:shared_ptr, реализован с использованием атомного счетчика, определенного в boost/interprocess/detail/atomic.hpp, а логика подсчета в основном реализована boost/interprocess/smart_ptr/detail/sp_counted_base_atomic.hpp. Намерение состоит в том, чтобы сделать refcount обработанным потоком (и межпроцессным) безопасным способом.

Реализации атомарных операций различаются в зависимости от конкретной целевой платформы (Windows использует Win32 Interlocked API, некоторые платформы используют различные встроенные сборки и т. Д.). Может быть полезно знать, на какую платформу вы ориентируетесь. Я полагаю, что вы можете столкнуться с ошибкой в ​​обработке refcount, хотя я бы на это не рассчитывал.

Я ограничил приведенный выше ответ областью, которую вы конкретно хотели адресовать:

Вопрос о безопасности потоков связан с подсчетом ссылок, т.е. разрешено ли копирование / уничтожение общих указателей на одну и ту же вещь в разных процессах одновременно. Нет доступа к одному и тому же общему указателю в разных потоках и нет доступа к pointee.

Тем не менее, я бы посмотрел на ошибки, возможно, вызванные элементами, которые вы упомянули выше, или каким-то образом создавая 'независимые' boost::interprocess:shared_ptr объекты (где разные shared_ptr ссылаются на один и тот же объект с использованием разных refcounts). Такая ситуация может легко возникнуть, если у вас есть код, который продолжает использовать и / или передавать необработанный указатель объекта.

1 голос
/ 24 апреля 2011

boost::shared_ptr<T> не является межпроцессным, поэтому вопрос о том, является ли он многопоточным, безопасен в этом контексте. (Это утверждение предполагает, что BOOST_SP_DISABLE_THREADS не было #defined для работы программы.)

boost::interprocess::shared_ptr<T> по своей природе спроектирован как межпроцессный безопасный, а также многопоточный безопасный по своей природе. Когда последняя ссылка выходит из области видимости, указанный объект может быть очищен. Очевидно, что эта очистка происходит в пределах сегмента общей памяти, используемого для объекта.

Поскольку boost::shared_ptr<T> использует механизм подсчета без блокировок в версии 1.33.0 на многих платформах, маловероятно, за исключением самых отдаленных шансов, что перекрестное удаление объекта в shared_memory Сегмент будет успешным, и, по-видимому, не будет поддерживать поддерживаемые функциональные возможности Boost.

0 голосов
/ 26 апреля 2011

Поскольку pgroke намекает (не уверен, почему это отрицательно), основной вопрос заключается в том, обращаетесь ли вы к одинаковому shared_ptr экземпляру из разных потоков или процессов.

shared_ptr (межпроцессный или нет) не поддерживает этот сценарий, и это не будет безопасно.

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

:: interprocess :: здесь в основном красная сельдь - она ​​не меняет потокобезопасность указателя, просто проверяет, нет ли внутренних указателей, которые ссылаются на частную память процесса и т. Д.

Так, какой из двух случаев это?

0 голосов
/ 25 апреля 2011

Просматривая код в shared_ptr.hpp и в документах на веб-сайте буста, кажется, что разыменование одного экземпляра может быть или не быть потокобезопасным в зависимости от второго параметра шаблона, который определяет тип внутреннего указателя на использоваться. В частности, «Внутренний указатель будет иметь тот же тип указателя, что и имя типа VoidAllocator :: pointer (то есть, если имя типа VoidAllocator :: pointer равно offset_ptr, внутренний указатель будет offset_ptr).» А поскольку разыменования просто возвращают результат метода get () / get_pointer () этого класса, он, вероятно, должен полностью зависеть от этого. Boost :: shared_ptr будет работать, если вы хотите одновременный доступ только для чтения. Для доступа к записи из нескольких потоков вам, возможно, придется написать свою собственную оболочку, смоделированную после offset_ptr.

0 голосов
/ 25 апреля 2011

"Кажется, это работает, в большинстве случаев, с использованием boost :: interprocess :: shared_ptr в сегменте boost :: fixed_managed_shared_memory: но не всегда." Если не всегда означает, что удаление не работает всегда: Просто используйте семафор с вашим потокобезопасным контейнером. Этот семафор не улучшает безопасность потоков, но вы можете проверить и даже ограничить, сколько пользователей использует данные. Если семафор равен 0, пользователь больше не может безопасно удалить общие данные. Если там только один пользователь, это будет 1, поэтому скопируйте запрошенные пользователем данные, удалите общий контейнер и вернитесь с копией.

0 голосов
/ 27 марта 2011

Er. boost::shared_ptr определенно не поточно-ориентированный. По крайней мере, не более поточно-ориентированный, чем, например, std::vector. Вы можете читать boost::shared_ptr из нескольких потоков, но как только какой-либо поток записывает boost::shared_ptr, он должен синхронизироваться с другими авторами и читателями.

И нет, вы не можете использовать его в общей памяти, он никогда не был предназначен для этого. Например. он использует так называемый объект «с общим счетом», в котором хранится счетчик ссылок и средство удаления, и этот объект с общим счетом выделяется кодом shared_ptr, поэтому он не будет находиться в общей памяти. Кроме того, код shared_ptr (и метаданные, такие как vtables) могут находиться по совершенно разным адресам в разных процессах, поэтому любой вызов виртуальной функции также будет проблемой (и IIRC shared_ptr использует виртуальные функции внутри - или, по крайней мере, указатели на функции, что приводит к той же проблеме).


Я не знаю , если boost::interprocess::shared_ptr безопасен для межпроцессных процессов, но я уверен, что это не так. Межпроцессная синхронизация довольно дорогая. Если boost::interprocess::shared_ptr , а не , это позволяет пользователю блокировать доступ к общим данным. Таким образом, высокая стоимость синхронизации оплачивается только один раз за несколько обращений подряд.

РЕДАКТИРОВАТЬ: Я бы ожидал , что шаблон использования, на который ссылался Имон Нербонн в своем комментарии (который является потокобезопасным с boost::shared_ptr), также в порядке с boost::interprocess::shared_ptr. Хотя точно сказать не могу.

...