【问题标题】:Using copy-on-write in multithreading environment in C++在 C++ 的多线程环境中使用写时复制
【发布时间】:2022-01-17 14:37:27
【问题描述】:

在我的 Qt C++ 应用程序中,我有两个主线程:GUI 线程和渲染线程。 GUI 线程管理可以创建和编辑的DataObject 结构列表,而渲染线程绘制对象(渲染线程无法修改数据对象)。我需要一种在两个线程之间共享数据对象的方法(渲染线程每帧可以渲染数千个对象,并且对象通常存储数百对浮点数)。 目前,我的设计策略包括存储 shared_ptr 的数据对象并将共享指针列表从 GUI 线程传递到渲染线程。当 GUI 线程正在修改数据对象并且渲染线程也试图读取相同的数据对象时,我使用互斥锁和锁来防止数据竞争。

另一方面,查看 Qt 源代码,我发现大量使用了 Copy-on-write 技术,该技术本质上涉及在只读访问时数据对象的浅拷贝和对于写访问的深拷贝。 Qt 还提供了方便的类,例如QSharedData 类来实现您自己的隐式共享对象。

因此,就我而言,我正在考虑使用写时复制,以便我可以使用这种技术实现我的DataObject。我可以直接传递DataObject 实例,而不是将共享指针传递给渲染线程(当然内部数据仍然使用指针存储),如果 GUI 线程试图修改数据对象,它只会复制数据并且这两个线程将继续运行,就好像它们拥有数据对象一样(渲染线程最终会丢弃它的副本)。 现在,在我看来,写时复制在 C++ 世界(Qt 之外)中非常不受欢迎,而且我还没有找到很多其他代码库中使用它的例子。在我的场景中使用写时复制有什么缺点?有什么我应该注意的陷阱吗?

【问题讨论】:

  • 您的“阅读”线程应该何时更新其“修改”数据?
  • 基本上我的 GUI 线程的每一帧都会发送一个要渲染的数据对象列表。所以渲染线程会在下一帧开始时接收到修改后的数据。
  • 我强烈推荐 stlab::copy_on_writegithub.com/stlab/libraries/blob/main/stlab/copy_on_write.hpp 与 Qt 不同的是,它明确说明了潜在副本的位置。

标签: c++ multithreading qt copy-on-write


【解决方案1】:

有一种非常简单的方法可以在某种程度上使锁变得不必要:

  • 在创建在某个时刻与不同线程共享的对象时,请使用std::unique_ptr<T>。这确保只有有限数量的代码可以访问该对象。请注意,您显然在此阶段不共享指针,否则这不起作用。总之,在写作时,您需要独占访问权限。
  • 如果你想将对象传递给另一个线程来处理它,你可以使用std::shared_ptr<T const>。这允许共享并确保最后一个所有者在使用后删除动态资源。这里的限制是它只允许只读访问。总之,你放弃写作是因为你想要共享访问。

注意事项:

  • 您需要严格控制 const 的正确性。如果对象 x 包含指针 p,您仍然可以使用该指针进行写入,即使 x 是对 const 的引用。指针本身将是常量,但指针对象不会继承它。这在使用智能指针时也一样!
  • 如果您将对象传递给具有所有权转移语义的线程,您可以为此使用unique_ptr。由于独占所有权,这也使得对象上的互斥锁同步变得不必要。典型应用是使用带有参数和结果的“任务”对象。一个线程(生产者)使用参数创建任务对象,然后另一个线程获得所有权(消费者)并计算结果。然后它通常将对象返回给初始生产者或将其传递给其他地方,充当生产者本身。
  • 所有这些都没有解决您在线程之间传输(唯一或共享)指针的方式。这仍然需要以线程安全的方式完成,通常涉及队列。

【讨论】:

  • 在您提出的解决方案中,我必须使用生产者-消费者成语之类的东西,对吗?否则,我看不出如何避免数据竞争。我有点不情愿采用这种方法,因为在我看来 GUI 线程应该是数据对象的唯一所有者,所以我不希望将所有权转移给另一个线程(其他线程只需要只读访问权限)。
  • 不,您没有使用生产-消费者设置。重要的区别是只有独占数据是可变的,而共享数据是常量。然后,您可以将共享数据广播到多个线程并在没有数据竞争的情况下访问它。但是,请记住,您绝对不能保留允许更改共享数据的引用(任何类型,例如智能指针)。也就是说,“GUI 线程应该是唯一的所有者”与“其他线程只需要只读访问”相矛盾:这是共享所有权,至少暂时如此。
猜你喜欢
  • 2016-11-10
  • 2017-11-23
  • 1970-01-01
  • 1970-01-01
  • 2010-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多