【问题标题】:How to futureproof thread-safe concurrent access to std::shared_ptr std::unique_ptr如何保证对 std::shared_ptr std::unique_ptr 的线程安全并发访问
【发布时间】:2021-12-19 19:30:27
【问题描述】:

随着 C++ 语言规范在该领域的发展,有哪些推荐的策略来应对当前 C++ 编码并发访问 std::shared_ptr(-like) 和 std::unique_ptr(-like) 数据结构?

背景:

大约 2021 年,用于以并发友好的方式管理对 std::shared_ptr(-like) 和 std::unique_ptr(-like) 智能指针的访问的可用 C++ 语言结构不断变化。例如,C++20 对 std::atomic<std::shared_ptr> 的支持还没有在编译器中走得太远,但 C++20 规范告诉我们它即将到来。

我从事重要的多线程开发,需要能够通过(希望是无锁的)队列在线程之间传递智能指针(共享的和唯一的),并以各种线程安全的方式使用它们。我想以一种允许它在可用时轻松升级到现代语言功能的方式开发此代码。理想的情况是能够从中心位置轻松地进行此升级,例如更改 CPP 宏的定义并根据这些宏进行编码。

有没有人知道一个很好的策略(也许是一组好的 CPP 宏?)用于面向未来的并发代码?

[在一些好的 cmets 之后澄清编辑(谢谢大家)] 从我收集到的:

  1. std::shared_ptrstd::unique_ptr 的不同实例可以从不同的线程毫无问题地读取/写入,(例如当不同的实例被传递到不同的线程时)但它们指向的对象实例(或内存)多个线程可能无法同时安全地访问(因此,如果这是用例,您应该使用互斥锁或其他方法来访问指向的对象)。 [感谢 Alex Guteniev 的澄清]
  2. std::shared_ptr 或 std::unique_ptr 的 SAME 实例可以使用(C++ 20 之前的:std::atomic_load/store 等和 C++20 之后的: std::atomic<std::shared_ptr>std::atomic<std::unique_ptr> ) 我的想法是,这可能是一个使用 CPP MACROS 的地方,例如 SHARED_GET、SHARED_SET、UNIQUE_GET、UNIQUE_SET,它们可以集中您从 C 开始需要进行的更改++17 到 C++20。 [感谢 NicolBolas 清楚地了解 C++20 中实际出现的内容。正如所指出的:我在下面的 cmets 中提供的链接已过时,因此请注意不要将其视为事实。]
  1. 如果您使用std::move 在线程之间传递std::unique_ptr 以传递指向的内存,并使用队列来强制在任何给定时间只有一个线程可以访问,您可以同时使用std_unique 指针他们自己和线程中的指向内存,在没有任何互斥锁或其他防止资源争用的保护的情况下接收指针。

由于我在提问时感到困惑,所以我最初的问题可能令人困惑。现在我将问题改写为:我正在寻找一组访问 CPP 宏#defines,它可以检测 C++17 和 C++20,并将该版本的最干净/正确的定义用于以下操作:

  • MAKE_LOCAL_SHARED:创建/加载本地 std::shared_ptr 实例 线程可以读取/写入的公共/共享实例 与原作争执。它应该指向相同的内存 共同/共享的指向

  • BEGIN_USE_SHARED_TGT:创建一个 在其范围内持有std::lock_guard/mutex 本地std::shared_ptr 实例的指向内存可以安全地 用过。

  • END_USE_SHARED_TGT:(可能只是一个右括号?)释放 使用指向内存完成时的std::lock_guard/mutex

  • BEGIN_USE_UNIQUE_TGT、END_USE_UNIQUE_TGT(同上 std::unique_ptr

【问题讨论】:

  • 您可以使用模板化类型别名,如果支持则引用 std::atomic<std::shared_ptr<T>>,如果不支持,则可以使用替代阻止或特定于平台的解决方案。只需确保您的替代方案与std::atomic<std::shared_ptr<T>> 接口兼容。
  • 我猜未来的 C++ 将保持与当前对线程安全对象的要求的向后兼容性,除非线程模型发生变化以至于不可能向后兼容.
  • @Dave: "用于以并发友好的方式管理对 std::shared_ptr(-like) 和 std::unique_ptr(-like) 智能指针的访问的 C++ 语言结构正在不断变化”。不,他们不是。 atomic<shared_ptr> 只是对 C++11 已经允许的东西的更好包装。即便如此,这有点无关紧要,因为atomic<shared_ptr> 是关于允许多个线程对特定的shared_ptr 对象进行原子访问,而不是它指向的对象。这与简单地将 shared_ptr 传递给另一个线程不同。
  • @NicolBolas, atomic 只是一个更好的 C++11 已经允许的包装器 - 不一定,因为 atomic<shared_ptr> 可以是无锁的。但否则同意
  • @Dave:没有atomic_shared_ptr<Type> 类型。那篇文章是 2017 年的,那是在谈论提案的早期版本。委员会采纳了该提案并将类型重命名为 atomic<shared_ptr<T>> 以明确它是什么。

标签: c++ multithreading shared-ptr c++20 unique-ptr


【解决方案1】:

我从事重要的多线程开发,需要能够通过(希望是无锁的)队列在线程之间传递智能指针(共享的和唯一的),并以各种线程安全的方式使用它们。

使用(无锁)队列时,您不会同时访问生产者和消费者元素。

对于访问不同的变量unique_ptrshared_ptr 已经是安全的。当两个shared_ptrs 指向同一个对象时,可以保证在不同线程中操作这些shared_ptrs 是线程安全的,通常使用引用计数来实现。不同的unique_ptrs 不指向同一个对象。

如果您只是将shared_ptrunique_ptr 放在一个队列中,而不是真正同时从多个线程访问同一个变量,则只需照常使用它们即可。

【讨论】:

  • 感谢您指出这一点!我最初采用了严格的 unique_ptr ONLY 方法,使用 1x1 队列,因此哪个线程可以访问 unique_ptr 以及它指向的内容非常明显。这主要是因为我不完全相信我了解所有不同的共享/独特/原子组合(请参阅上面对 NicolBolas 的评论以了解不同的风格)。当我想将它发送到多个下游处理线程时,我想做的是放宽复制 unique_ptr 的要求。我想把它变成一个 shared_ptr (避免复制)并发送它。
  • 我关于未来证明的问题是集中我实现的任何模式,以便在 C++20 出现时对其进行更新,但前提是我将真正需要这些新类型。在未来到来之前,我还要理清现在! :)
  • @Dave,如果你愿意,你可以包装template<T> using SharedPtr = std::share_ptr<T>。但我不认为这是必要的。 std::atomic<std::shared_ptr<T>> 不是 std::shared_ptr<T> 的替代品,而是受互斥锁保护的 std::shared_ptr<T> 的替代品。
猜你喜欢
  • 2013-05-05
  • 1970-01-01
  • 2013-01-07
  • 2019-06-07
  • 1970-01-01
  • 2021-03-27
  • 2015-07-23
相关资源
最近更新 更多