如何正确实现三重缓冲?
How to correctly implement triple buffering?
我正在尝试模拟视频卡(生产者线程)和监视器(消费者线程),以弄清楚在教育目的上发生了什么。所以这是技术任务
描述:
生产者线程以 1000 fps 的速度产生帧像素数据。消费者线程以 60 fps 的速度运行,并且每一帧它必须至少有 1/60 秒的时间访问最后生成的帧。为简单起见,每一帧都由一些 int*
表示。
所以我的解决方案是我有 2 个指针的数组:一个用于生产者,一个用于消费者。再加上一些免费的、未使用的指针,在任何给定的时间都不为消费者或生产者所有。
#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]
。
如有错误请指正
我想念memorySemanticsXXX
。 There are descriptions 但我无法弄清楚我应该在每个线程中使用哪个以及为什么。所以我想就此寻求一些提示。
memorySemanticsXXX
您提到的是围绕 exchange()
行的其余代码。 std::atomic::exchange()
的默认行为是使用 memory_order_seq_cst
(您未使用 exchange()
的第二个参数)。
这同时意味着三件事:
- 您在
exchange()
调用之前编写的任何代码都保证在该调用之前执行(否则编译器优化可以重新排序您的代码)和 该执行的结果将在进行 exchange()
调用之前在所有其他线程中可见(CPU 缓存传播)。
与前面相同,但您在 exchange()
行之后编写的代码。
exchange()
调用前后的所有代码都按照您编写的确切顺序执行(包括其他原子操作)。
所以,重点是您可以选择不设置这些限制中的一个、两个或所有三个,这 可以 为您带来速度提升。你不应该为这个 烦恼,除非你有性能瓶颈 。如果没有瓶颈,那么只需使用不带第二个参数的 std::atomic
(它将采用默认值)。
如果您没有使用所有三个限制,则在编写代码时必须非常小心,否则它可能会意外崩溃。
在此处阅读更多相关信息:Memory order
我正在尝试模拟视频卡(生产者线程)和监视器(消费者线程),以弄清楚在教育目的上发生了什么。所以这是技术任务 描述:
生产者线程以 1000 fps 的速度产生帧像素数据。消费者线程以 60 fps 的速度运行,并且每一帧它必须至少有 1/60 秒的时间访问最后生成的帧。为简单起见,每一帧都由一些 int*
表示。
所以我的解决方案是我有 2 个指针的数组:一个用于生产者,一个用于消费者。再加上一些免费的、未使用的指针,在任何给定的时间都不为消费者或生产者所有。
#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]
。
如有错误请指正
我想念memorySemanticsXXX
。 There are descriptions 但我无法弄清楚我应该在每个线程中使用哪个以及为什么。所以我想就此寻求一些提示。
memorySemanticsXXX
您提到的是围绕 exchange()
行的其余代码。 std::atomic::exchange()
的默认行为是使用 memory_order_seq_cst
(您未使用 exchange()
的第二个参数)。
这同时意味着三件事:
- 您在
exchange()
调用之前编写的任何代码都保证在该调用之前执行(否则编译器优化可以重新排序您的代码)和 该执行的结果将在进行exchange()
调用之前在所有其他线程中可见(CPU 缓存传播)。 与前面相同,但您在
exchange()
行之后编写的代码。exchange()
调用前后的所有代码都按照您编写的确切顺序执行(包括其他原子操作)。
所以,重点是您可以选择不设置这些限制中的一个、两个或所有三个,这 可以 为您带来速度提升。你不应该为这个 烦恼,除非你有性能瓶颈 。如果没有瓶颈,那么只需使用不带第二个参数的 std::atomic
(它将采用默认值)。
如果您没有使用所有三个限制,则在编写代码时必须非常小心,否则它可能会意外崩溃。
在此处阅读更多相关信息:Memory order