我正在尝试模拟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]
。
如果我错了,请纠正我。
我想念memorySemanticsXXX
。 There are descriptions但我无法弄清楚我应该在每个线程中使用哪个以及为什么。所以我要求提供一些提示。
答案 0 :(得分:1)
memorySemanticsXXX
您提到的是围绕exchange()
行的其余代码。 std::atomic::exchange()
的默认行为是使用memory_order_seq_cst
(您未使用的exchange()
的第二个参数)。
这意味着同时有三件事:
exchange()
调用之前编写的任何代码都保证在调用之前执行(否则编译器优化可以重新排序代码)和执行的结果将在所有其他代码中可见进行exchange()
调用之前的线程(CPU缓存传播)。与之前相同,但是您在exchange()
行之后编写的代码。
exchange()
调用之前和之后的所有代码都按照您编写它的确切顺序执行(包括其他原子操作)。
所以,重点是你可以选择不拥有这些限制中的一个,两个或全部,可以为你带来速度提升。你不应该为这个打扰,除非你有性能瓶颈。如果没有瓶颈,那么只需使用std::atomic
而不使用第二个参数(它将采用默认值)。
如果您不使用所有三个限制,您必须非常小心地编写代码,否则会无法预料地崩溃。
在此处详细了解:Memory order