【问题标题】:Is boost::interprocess::shared_ptr threadsafe (and interprocess-safe)?boost::interprocess::shared_ptr 线程安全(和进程间安全)吗?
【发布时间】:2011-03-20 00:35:11
【问题描述】:

我想在线程之间共享数据,并在最后一个用户完成后自动删除它。在大多数情况下,这似乎有效,在 boost::fixed_managed_shared_memory 段中使用 boost::interprocess::shared_ptr:但并非总是如此。

那么,boost::interprocess::shared_ptr 线程(和进程间)安全吗?

如果我在一个固定地址使用我的共享内存(我很确定这在我的 64 位(嗯,48 位)地址空间中是可以的),是否可以使用普通的boost::shared_ptr(它们是线程安全的)?

一些澄清:

我使用的指针类型是普通的void*,(我的共享内存映射到一个固定地址)。

线程安全问题与引用计数有关——即,是否允许同时在不同进程中复制/销毁指向同一事物的共享指针。 访问不同线程中的同一个共享指针,并且访问被指针对象。

【问题讨论】:

  • 我不知道细节,但如果 boost::interprocess::shared_ptr 不是进程间安全的,这似乎是相当矛盾的。他们为什么要把它放在那里?
  • 因为它可以放在 共享内存中,而 boost::shared_ptr 不能(因为它使用虚拟继承等)。
  • 仍然,这意味着您需要手动同步该 shared_ptr 的生命周期结束?这完全违反了 RAII,所以它们当然应该和通常的 shared_ptr 一样是线程安全的。
  • 是的,我希望它是。我真的希望有人确认它是安全的(最好有一些证据),所以我可以将我的崩溃归咎于我的代码......
  • 请参阅下面的评论 - 您需要澄清您是否正在从多个线程或进程访问特定的 shared_ptr 实例(谈论指针本身,而不是指向的对象)。这不安全。

标签: c++ boost parallel-processing boost-interprocess


【解决方案1】:

boost::interprocess:shared_ptr 中使用的引用计数是使用boost/interprocess/detail/atomic.hpp 中定义的原子计数器实现的,引用计数逻辑主要由boost/interprocess/smart_ptr/detail/sp_counted_base_atomic.hpp 实现。目的是以线程(和进程间)安全的方式处理引用计数。

原子操作的实现因具体目标平台而异(Windows 使用 Win32 Interlocked API,某些平台使用各种内联汇编等)。了解您的目标平台可能会有所帮助。我想您可能在引用计数处理中遇到了一个错误,尽管我不会指望它。

我已将上述答案限制在您想要特别解决的领域:

线程安全问题与引用计数有关——即,是否允许同时在不同进程中复制/销毁指向同一事物的共享指针。不同线程不能访问同一个共享指针,也不能访问被指针。

也就是说,我会查看可能由您上面提到的项目或通过以某种方式创建“独立”boost::interprocess:shared_ptr 对象(其中不同的shared_ptrs 使用不同的引用计数引用同一对象)引入的错误。如果您有一些代码继续使用和/或传递原始对象指针,则很容易发生这种情况。

【讨论】:

  • 谢谢,这就是我想知道的!
【解决方案2】:

boost::shared_ptr<T> 不是进程间安全的,所以在这种情况下它是否是多线程安全的还没有实际意义。 (此语句假定BOOST_SP_DISABLE_THREADS 尚未成为#defined 用于程序的操作。)

boost::interprocess::shared_ptr<T> 本质上是为跨进程安全和多线程安全而设计的。当最后一个引用超出范围时,可以清理指向的对象。显然,这种清理发生在用于对象的共享内存段的范围内。

由于boost::shared_ptr<T> 在许多平台上使用lock-free counting mechanism at version 1.33.0,除非极有可能跨进程删除shared_memory 段中的对象会成功,并且似乎不受支持的功能由 Boost 维护者提供。

【讨论】:

  • 为什么锁定本身不意味着进程间安全?
  • 并非所有平台都使用无锁方式,boost 的维护者不保证boost::shared_ptr<T> 今后会继续使用无锁机制。最重要的是,boost::shared_ptr<T> 的合同中不存在跨进程保证,因为它适用于 boost::interprocess::shared_ptr<T>
【解决方案3】:

呃。 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 这样做可以让用户阻止对共享数据的访问。这样,连续多次访问只需支付一次高同步成本。

编辑:我期望 Eamon Nerbonne 在他的评论中提到的使用模式(对于boost::shared_ptr 是线程安全的),对于boost::interprocess::shared_ptr 也是可以的。不过不能肯定。

【讨论】:

  • boost::shared_ptr is threadsafe 意味着多个 distinct 实例恰好引用同一个共享指针可以同时读取和修改;这已经足够有用了。
  • 您的意思是指向同一个指针的多个共享指针(不是同一个共享指针)。我对此很清楚。当然,有用就足够了。然而,仅仅声称 shared_ptr “是线程安全的”(无需进一步解释)是不够的,因为正如我所写的,某些使用场景不是。而且我相信 OP 并没有意识到这一点。
  • 那么你真的应该更新这个答案;因为这是一个相当重要的线程安全级别 - 例如,保证 std::vector 没有等价物(所以它不是一个很好的例子)。
  • 是的,这是一种有用的线程安全(线程安全共享计数)。我很欣赏线程安全是一个定义模糊的术语。
【解决方案4】:

“在 boost::fixed_managed_shared_memory 段中使用 boost::interprocess::shared_ptr 似乎大多数时候都有效:但并非总是如此。” 如果不总是意味着删除并不总是有效: 只需在线程安全容器中使用信号量即可。这个信号量并不能提高线程安全性,但可以验证甚至限制有多少用户使用数据。如果 semaphore 为 0,则没有更多用户,安全删除共享数据。 如果只有一个用户,这将是 1,所以复制出用户请求的数据,删除共享容器,然后返回副本。

【讨论】:

    【解决方案5】:

    查看 shared_ptr.hpp 中的代码以及 boost 网站上的文档,似乎取消引用单个实例可能是线程安全的,也可能不是线程安全的,具体取决于第二个模板参数,它决定了内部指针类型使用。具体来说, “内部指针将与 typename VoidAllocator::pointer 类型相同(即,如果 typename VoidAllocator::pointer 为 offset_ptr,则内部指针将为 offset_ptr)。” 而且由于取消引用仅返回此类的 get()/get_pointer() 方法的结果,因此它可能应该完全依赖于此。如果您想要同时进行只读访问,则 Boost::shared_ptr 将起作用。对于来自多个线程的写访问,您可能必须编写自己的以 offset_ptr 为模型的包装器。

    【讨论】:

    • 我使用的指针类型是普通的void*,(我的共享内存映射到固定地址)。线程安全问题与引用计数有关——即,在不同进程中同时复制/销毁指向同一事物的共享指针。
    • 引用计数是否在进程间同步?我得检查一下。
    【解决方案6】:

    正如 pgroke 所暗示的(不知道为什么不赞成),核心问题是您是否从不同的线程或进程访问 same shared_ptr 实例。

    shared_ptr (interprocess or not) 不支持这种情况,这不安全。

    另一方面,shared_ptr 被设计成有多个(线程私有的,或通过其他机制防止并发修改的)共享指针实例指向同一个对象,并且这些指针的不同实例指向同一个对象同时修改没有问题。

    ::interprocess:: 这里主要是一个红鲱鱼 - 它不会改变指针的线程安全性,只是确保没有引用进程私有内存等的内部指针。

    那么这两种情况是哪一种呢?

    【讨论】:

    • 进程间有一个完全独立的共享指针实现:就我关心的方面而言,普通的共享指针是完全线程安全的:我正在复制/销毁指向同一事物的不同进程中的不同共享指针(所以共享一个引用计数)。 (但是,boost::shared_ptr 不能放在共享内存中,因为它使用虚拟继承等。
    • 有了这个澄清,那么 shared_ptr 的两个版本(进程间和非进程间)都应该是安全的,因为只要启用了正确的标志,它们就会使用原子操作(参见,例如,各种平台特定的标头包括here)。
    猜你喜欢
    • 2012-03-05
    • 1970-01-01
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 2013-01-07
    • 2013-09-24
    相关资源
    最近更新 更多