繁体   English   中英

在 C++ 的多线程环境中使用写时复制

[英]Using copy-on-write in multithreading environment in C++

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

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

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

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

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

笔记:

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

在这种情况下使用写时复制语义可能很有用,但性能(提升)可能取决于每个渲染周期之间更改的数据量。 如果更改了很多数据,几乎所有的数据都会被复制。 首先将数据复制(或移动)到渲染线程可能会更有效,因为它避免了写时复制检查的开销。 使用写时复制行为的另一个缺点是隐式共享迭代器问题,当隐式共享 object 在迭代器仍处于活动状态时被分离时,就会出现这种问题。

使用写时复制,在您的情况下具有一些优势(与互斥锁和/或深复制到渲染线程相比):

  • 无锁
  • 渲染线程看不到 GUI 线程在渲染周期中所做的(部分)更改。
  • 尽可能避免复制数据(如果大多数数据在两个连续的渲染周期之间保持不变,则特别有用。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM