这里需要挥发性

tte*_*ple 4 c++ multithreading volatile

我正在实现一个'序列锁'类,以允许锁定写入和无锁读取数据结构.

包含数据的结构包含序列值,在写入发生时,该值将递增两次.在写作开始之前,写作完成之后一次.作者在读者之外的其他线程上.

这是包含数据副本的结构,序列值如下所示:

template<typename T>
struct seq_data_t
{
    seq_data_t() : seq(0) {};
    int seq;                     <- should this be 'volatile int seq;'?
    T data;
};
Run Code Online (Sandbox Code Playgroud)

整个序列锁定类在循环缓冲区中保存此结构的N个副本.编写器线程总是在循环缓冲区中写入最旧的数据副本,然后将其标记为当前副本.写作是互斥锁定的.

读取功能不会锁定.它试图读取数据的"当前"副本.它在读取之前存储'seq'值.然后它读取数据.然后它再次读取seq值,并将其与第一次读取的值进行比较.如果seq值没有改变,则认为读取是好的.

由于写入线程可以在读取发生时更改"seq"的值,因此我认为seq变量应该标记为volatile,以便read函数在读取数据后将显式读取该值.

read函数如下所示:它将在除writer之外的线程上,也许还有几个线程.

    void read(std::function<void(T*)>read_function)
    {
        for (;;)
        {
            seq_data_type<T>* d = _data.current; // get current copy
            int seq1 = d->seq;      // store starting seq no
            if (seq1 % 2)           // if odd, being modified...
                continue;           //     loop back

            read_function(&d->data);  // call the passed in read function
                                      // passing it our data.


//??????? could this read be optimized out if seq is not volatile?
            int seq2 = d->seq;      // <-- does this require that seq be volatile?
//???????

            if (seq1 == seq2)       // if still the same, good.
                return;             // if not the same, we will stay in this
                                    // loop until this condition is met.
        }
    }
Run Code Online (Sandbox Code Playgroud)

问题:

1)在这种情况下,seq必须是易变的吗?

2)在具有多个成员的struct的上下文中,只有volatile的合格变量volatile,而不是其他成员?ie,如果我只在结构中标记它是volatile,那么它只是'seq'volatile?

cma*_*ter 5

不要使用volatile,使用std::atomic<>.volatile设计用于与内存映射硬件交互,std::atomic<>旨在用于线程同步.使用正确的工具完成工作.

良好std::atomic<>实施的特点:

  • 它们对于标准整数类型是无锁的(long long通常是一切).

  • 它们适用于任何数据类型,但将使用透明互斥锁来处理复杂数据类型.

  • 如果std::atomic<>是无锁的,它会插入正确的内存屏障/栅栏以实现正确的语义.

  • 操作std::atomic<>无法优化,它们毕竟是为线程间通信而设计的.

  • @AndriyBerestovskyy是什么让你认为性能黑客不应该是可移植的(据我所知,这是一个)? (2认同)
  • @AndriyBerestovskyy我不认为,这是关于便携性.它与速度和正确性有关.如果你想实现一个快速的Seqlock,你需要使用一个原子类型,否则你可能会得到序列号的无意义读取,**和原子类型是确保有效读取序列号**的最快方法. (2认同)
  • @ttemple正如我所说,原子是**专门用于线程间通信**.这意味着编译器不得a)优化远离读/写,或b)发明读/写.如果是这样,您将无法生成依赖于原子的有效并行代码. (2认同)