易失成员变量与易失对象? [英] Volatile member variables vs. volatile object?

查看:66
本文介绍了易失成员变量与易失对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在下面的 MpscQueue.h中的嵌入式目标上实现多个生产者(通过中断),单个消费者(通过应用程序线程)队列。

I'm trying to implement a multiple producer (via interrupt), single consumer (via application thread) queue on an embedded target in "MpscQueue.h" below.

我想知道我是否可以安全地删除以下一些 volatile 用法(请参阅内联问题)。我还考虑使用 volatile std :: array 代替下面所示的C风格的 buffer _ [] ,但是我不确定我是否可以相信它的实现以符合以下目的。第三种选择是将MpscQueue对象本身标记为 volatile 并限定相关方法 volatile ,但目前尚不清楚

I'm wondering if I can safely remove the some of the volatile usage below (see inline questions). I would also consider using volatile std::array in place of the C-style buffer_[] shown below, but I was unsure if I could trust its implementation to match the intent of the below. A third alternative would be to mark the MpscQueue object itself as volatile and qualify the relevant methods volatile, but it's not clear if that would result in all member variables (and what they point to, in the case of pointers) being treated as volatile.

对此是否有任何指导?

template<typename T, uint32_t depth>
class MpscQueue
{
public:
    MpscQueue(void);
    bool push(T& t);
    bool pop(T* const t);

private:
    T volatile buffer_[depth];  // Q1: is volatile unnecessary if never access buffer_[]?
    T volatile* const begin_;   // Q2: is volatile unnecessary if never access value
    T volatile* const end_;     //     via begin_/end_?
    T volatile* head_;          // volatile required so that thread always checks value
    T volatile* volatile tail_; // Q3: is 'T volatile' required so that ISR accounts
                                //     for other ISRs when setting value?
                                // Q4: is '* volatile' required so that ISR accounts
                                //     for other ISRs when checking pointer?
};

template<typename T, uint32_t depth>
MpscQueue<T, depth>::MpscQueue(void) :
    begin_(&buffer_[0]),
    end_(&buffer_[depth - 1]),
    head_(begin_),
    tail_(begin_)
{}

template<typename T, uint32_t depth>
bool MpscQueue<T, depth>::push(T& t)
{
    // "Multiple producer" ISRs can use this function to push at tail

    // Pseudo-code: if not full, *(tail_++) = t
}

template<typename T, uint32_t depth>
bool MpscQueue<T, depth>::pop(T* const t)
{
    // "Single consumer" thread can use this function to pop at head

    // Pseudo-code: if not empty, *t = *(head_++)
}






编辑:为了将问题聚焦在正确的方向上,让我澄清一下,我已经注意了线程安全性,这不是这里问题的一部分。


To focus the question in the right direction, let me clarify that I have taken care of thread-safety, and it is not part of the question here.

由于此队列是单个使用者,因此读/弹出端不需要线程安全。在写/推方面,将所有相关中断设置为相同的优先级来处理中断之间的线程安全(否则,将使用锁定)。

Because this queue is single consumer, there is no thread safety required on the read/pop side. On the write/push side, thread safety among interrupts will be handled by setting all relevant interrupts to the same priority level (otherwise, a lock will be used).

推荐答案

所编写的代码不是中断安全的-如果在主线程执行读/弹出操作时发生中断,则说明您处于竞争状态,并且数据结构可能已损坏。解决此问题的方法是每当它执行读/弹出操作时,就在主线程中阻止中断。如果这样做(阻塞/解除阻塞的功能是内存障碍),则所有的挥发物都变得无关紧要,可以将其删除。

As written the code is not interrupt-safe -- if an interrupt occurs while the main thread is doing a read/pop, you have a race condition, and the data structure is likely to be corrupted. The way to fix this is to block interrupts in the main the thread whenever it does a read/pop. If you do that (and the functions that block/unblock interrupts are memory barrieres), the volatiles all become irrelevant and can be removed.

Volatile对线程几乎没有用处同步-它的主要用途是与内存映射的设备进行交互。

Volatile is pretty much useless for thread synchronization -- its primary use is for interacting with memory-mapped devices.

这篇关于易失成员变量与易失对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆