如何正确实现三重缓冲?

时间:2018-03-18 21:13:08

标签: multithreading synchronization producer-consumer lock-free buffering

我正在尝试模拟videocard(生产者线程)和监视器(消费者线程),以弄清楚教育目的是什么。所以这是技术任务 说明:

生产者线程以1000 fps生成帧像素数据。消费者线程以60 fps运行,每帧必须能够访问最后生成的帧至少1/60秒。为简单起见,每个框架由一些int*表示。

所以我的解决方案是我有两个指针数组:一个用于生产者,一个用于消费者。加上一些免费的,未使用的指针,在任何特定时刻都不属于消费者或生产者。

#define Producer 0
#define Consumer 1
int* usedPointers[2];
std::atomic<int*> freePointer;
  • 生成器始终将帧像素写入usedPointers[Producer],然后执行usedPointers[Producer] = freePointer.exchange(usedPointers[Producer], memorySemanticsProducer);,以便最后完全生成的帧现在由freePointer指向,并且可以自由地写入新帧,而不是摧毁最后一个实际完整的框架。

  • 消费者执行usedPointers[Consumer] = freePointer.exchange(usedPointers[Consumer], memorySemanticsConsumer);以便它拥有最后的实际帧数据,然后可以根据需要随意访问usedPointers[Consumer]

如果我错了,请纠正我。

我想念memorySemanticsXXXThere are descriptions但我无法弄清楚我应该在每个线程中使用哪个以及为什么。所以我要求提供一些提示。

1 个答案:

答案 0 :(得分:1)

memorySemanticsXXX您提到的是围绕exchange()行的其余代码。 std::atomic::exchange()的默认行为是使用memory_order_seq_cst(您未使用的exchange()的第二个参数)。

这意味着同时有三件事:

  • exchange()调用之前编写的任何代码都保证在调用之前执行(否则编译器优化可以重新排序代码)执行的结果将在所有其他代码中可见进行exchange()调用之前的线程(CPU缓存传播)。
  • 与之前相同,但是您在exchange()行之后编写的代码。

  • exchange()调用之前和之后的所有代码都按照您编写它的确切顺序执行(包括其他原子操作)。

所以,重点是你可以选择不拥有这些限制中的一个,两个或全部,可以为你带来速度提升。你不应该为这个打扰,除非你有性能瓶颈。如果没有瓶颈,那么只需使用std::atomic而不使用第二个参数(它将采用默认值)。

如果您不使用所有三个限制,您必须非常小心地编写代码,否则会无法预料地崩溃。

在此处详细了解:Memory order