问题
I am trying to simulate videocard (producer thread) and a monitor(consumer thread), to figure out what is going on in educational purposes. So here is the technical task description:
Producer thread produces frames pixel data at 1000 fps. Consumer thread runs at 60 fps and every frame it must have access to last produced frame for at least 1/60th of second. Each frame is represented by some int*
, for simplicity.
So my solution is that i have array of 2 pointers: one for producer, one for consumer. And plus some free, unused pointer, which is not owned by consumer or producer at any given moment of time.
#define Producer 0
#define Consumer 1
int* usedPointers[2];
std::atomic<int*> freePointer;
producer always writes frame pixels to
usedPointers[Producer]
, then doesusedPointers[Producer] = freePointer.exchange(usedPointers[Producer], memorySemanticsProducer);
so that last completely produced frame is now pointed byfreePointer
, and its free to write new frame, not destroying last actual complete frame.consumer does
usedPointers[Consumer] = freePointer.exchange(usedPointers[Consumer], memorySemanticsConsumer);
so that it would own last actual frame data, and then is free to accessusedPointers[Consumer]
as long, as it desires to.
Correct me if i am wrong.
I miss what is memorySemanticsXXX
. There are descriptions but i cannot figure out which exactly should i use in every thread and why. So i am asking for some hints on that.
回答1:
memorySemanticsXXX
you're mentioning are about the rest of your code surrounding the exchange()
lines. The default behavior for std::atomic::exchange()
is that memory_order_seq_cst
is used (the second parameter for exchange()
you're not using).
This means three things at the same time:
- Any code you wrote before your
exchange()
call is guaranteed to execute before that call (otherwise compiler optimizations can reorder your code) and the results of that execution will be visible in all other threads (CPU cache propagation) before theexchange()
call is made. The same as previous but for the code you wrote after your
exchange()
line.All of the code before and after
exchange()
call is executed in the exact order you wrote it (including other atomic operations).
So, the whole point is that you may choose not to have one, two or all three of these restrictions, which can bring you speed improvements. You shouldn't bother with this unless you have a performance bottleneck. If there's no bottleneck then just use std::atomic
without the second parameter (it will take the default value).
In case you don't use all three restrictions you have to be really careful writing your code otherwise it can unpredictably crash.
Read more about it here: Memory order
来源:https://stackoverflow.com/questions/49352853/how-to-correctly-implement-triple-buffering