Я написал простой класс 'envelope', чтобы убедиться, что я правильно понимаю атомарную семантику C ++ 11.У меня есть заголовок и полезная нагрузка, где писатель очищает заголовок, заполняет полезную нагрузку, а затем заполняет заголовок возрастающим целым числом.Идея состоит в том, что читатель может затем прочитать заголовок, запомнить полезную нагрузку, снова прочитать заголовок, и, если заголовок тот же, читатель может предположить, что он успешно скопировал полезную нагрузку.Это нормально, что читатель может пропустить некоторые обновления, но это не нормально для них, чтобы получить разорванное обновление (где есть смесь байтов из разных обновлений).Существует только один читатель и один писатель.
Писатель использует порядок освобождения памяти, а считыватель использует порядок получения памяти.
Есть ли риск того, что memcpy будет переупорядочен с атомарнымхранить / загружать звонки?Или нагрузки могут быть переупорядочены друг с другом?Это никогда не прерывается для меня, но, может быть, мне повезло.
#include <iostream>
#include <atomic>
#include <thread>
#include <cstring>
struct envelope {
alignas(64) uint64_t writer_sequence_number = 1;
std::atomic<uint64_t> sequence_number;
char payload[5000];
void start_writing()
{
sequence_number.store(0, std::memory_order::memory_order_release);
}
void publish()
{
sequence_number.store(++writer_sequence_number, std::memory_order::memory_order_release);
}
bool try_copy(char* copy)
{
auto before = sequence_number.load(std::memory_order::memory_order_acquire);
if(!before) {
return false;
}
::memcpy(copy, payload, 5000);
auto after = sequence_number.load(std::memory_order::memory_order_acquire);
return before == after;
}
};
envelope g_envelope;
void reader_thread()
{
char local_copy[5000];
unsigned messages_received = 0;
while(true) {
if(g_envelope.try_copy(local_copy)) {
for(int i = 0; i < 5000; ++i) {
// if there is no tearing we should only see the same letter over and over
if(local_copy[i] != local_copy[0]) {
abort();
}
}
if(messages_received++ % 64 == 0) {
std::cout << "successfully received=" << messages_received << std::endl;
}
}
}
}
void writer_thread()
{
const char alphabet[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
unsigned i = 0;
while(true) {
char to_write = alphabet[i % (sizeof(alphabet)-1)];
g_envelope.start_writing();
::memset(g_envelope.payload, to_write, 5000);
g_envelope.publish();
++i;
}
}
int main(int argc, char** argv)
{
std::thread writer(&writer_thread);
std::thread reader(&reader_thread);
writer.join();
reader.join();
return 0;
}