Является ли boost :: interprocess :: shared_ptr потокобезопасным (и межпроцессорным)?

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

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

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

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

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

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


person James    schedule 20.03.2011    source источник
comment
Я не знаю деталей, но это могло бы показаться довольно противоречивым, если бы boost :: interprocess :: shared_ptr не был безопасным для межпроцессного взаимодействия. Зачем им это там?   -  person CashCow    schedule 20.03.2011
comment
Потому что он может быть помещен в разделяемую память, тогда как boost::shared_ptr не может (поскольку он, помимо прочего, использует виртуальное наследование).   -  person James    schedule 20.03.2011
comment
тем не менее, это будет означать, что вам нужно вручную синхронизировать конец срока службы этого shared_ptr? Это полностью нарушает RAII, поэтому они, безусловно, должны быть такими же потокобезопасными, как и обычный shared_ptr.   -  person Eamon Nerbonne    schedule 27.03.2011
comment
Да, я так и ожидал. Я действительно хотел бы, чтобы кто-нибудь подтвердил, что это безопасно (желательно с некоторыми доказательствами), поэтому я могу обвинить свой код в сбоях ...   -  person James    schedule 25.04.2011
comment
См. Мой комментарий ниже - вам нужно уточнить, обращаетесь ли вы к конкретному экземпляру shared_ptr (речь идет о самом указателе, а не о указанном объекте) из нескольких потоков или процессов. Это небезопасно.   -  person BeeOnRope    schedule 26.04.2011
comment
См. Выше: я копирую / уничтожаю разные экземпляры общего указателя в разных процессах.   -  person James    schedule 26.04.2011


Ответы (6)


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

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

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

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

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

person Michael Burr    schedule 28.04.2011
comment
Спасибо, вот что я хотел знать! - person James; 28.04.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.

person Andy Finkenstadt    schedule 24.04.2011
comment
Почему отсутствие блокировки само по себе означает небезопасность между процессами? - person SoapBox; 25.04.2011
comment
Не все платформы используют метод блокировки без блокировки, и разработчики boost не гарантируют, что boost::shared_ptr<T> будет продолжать использовать механизм без блокировки в будущем. Наиболее важно то, что межпроцессная гарантия отсутствует в контракте для boost::shared_ptr<T>, как для boost::interprocess::shared_ptr<T>. - person Andy Finkenstadt; 28.04.2011

Э-э. 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. Хотя не могу сказать наверняка.

person Paul Groke    schedule 27.03.2011
comment
boost :: shared_ptr является потокобезопасным в ощущение, что несколько различных экземпляров, которые ссылаются на один и тот же общий указатель, могут одновременно считываться и изменяться; этого достаточно, чтобы быть полезным. - person Eamon Nerbonne; 27.03.2011
comment
Вы имеете в виду несколько общих указателей, которые указывают на один и тот же указатель (не на один и тот же общий указатель). Я знаю об этом. И, конечно, этого достаточно, чтобы быть полезным. Однако недостаточно утверждать, что shared_ptr является потокобезопасным (без дополнительных объяснений), потому что, как я уже писал, некоторые сценарии использования таковыми не являются. И я считаю, что ОП об этом не знает. - person Paul Groke; 27.03.2011
comment
Тогда вам действительно стоит обновить этот ответ; потому что это довольно важный уровень безопасности потоков - например, гарантия std::vector не имеет эквивалента (так что это не лучший пример для использования). - person Eamon Nerbonne; 27.03.2011
comment
Да, это тот вид поточно-ориентированного, который полезен (поточно-ориентированный общий счетчик). Я понимаю, что потокобезопасность - это расплывчатый термин. - person James; 27.03.2011

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

person Alexander Bránya    schedule 25.04.2011

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

person vagrantpostman    schedule 25.04.2011
comment
Тип указателя, который я использую, простой void* (моя общая память отображается на фиксированный адрес). Вопрос безопасности потоков связан с подсчетом ссылок, т. Е. Одновременным копированием / уничтожением общих указателей на одно и то же в разных процессах. - person James; 25.04.2011
comment
Refcount даже синхронизируется во время межпроцессного взаимодействия? Придется проверить. - person vagrantpostman; 25.04.2011

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

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

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

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

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

person BeeOnRope    schedule 26.04.2011
comment
Interprocess имеет полностью отдельную реализацию общего указателя: обычный общий указатель полностью потокобезопасен в том отношении, что меня волнует: я копирую / уничтожаю разные общие указатели в разных процессах, которые указывают на одно и то же (так что они имеют общий счетчик ссылок). (boost::shared_ptr, однако, не может быть помещен в общую память, потому что, помимо прочего, он использует виртуальное наследование. - person James; 26.04.2011
comment
С этим пояснением, тогда да, обе версии (межпроцессная и нет) shared_ptr должны быть безопасными для этого использования, поскольку они используют атомарные операции, пока включены соответствующие флаги (см., Например, различные заголовки, специфичные для платформы, включая здесь). - person BeeOnRope; 28.04.2011