[英]Volatile member variables vs. volatile object?
我正在尝试在下面的“MpscQueue.h”中的嵌入式目标上实现多生产者(通过中断)、单消费者(通过应用程序线程)队列。
我想知道我是否可以安全地删除下面的一些volatile
用法(请参阅内联问题)。 我还会考虑使用volatile std::array
代替下面显示的 C 样式buffer_[]
,但我不确定我是否可以相信它的实现与下面的意图相匹配。 第三种选择是将 MpscQueue 对象本身标记为volatile
并限定相关方法volatile
,但尚不清楚这是否会导致所有成员变量(以及它们指向的内容,在指针的情况下)被视为 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_++)
}
编辑:为了将问题集中在正确的方向上,让我澄清一下,我已经注意了线程安全,这不是这里问题的一部分。
因为这个队列是单消费者,所以在读取/弹出端不需要线程安全。 在写入/推送方面,中断之间的线程安全将通过将所有相关中断设置为相同的优先级来处理(否则,将使用锁)。
编写的代码不是中断安全的——如果在主线程执行读取/弹出操作时发生中断,则存在竞争条件,并且数据结构可能会损坏。 解决此问题的方法是在线程执行读取/弹出操作时阻止主线程中的中断。 如果您这样做(并且阻止/取消阻止中断的功能是内存屏障),则所有易失性都变得无关紧要并且可以删除。
Volatile 对于线程同步几乎没有用处——它的主要用途是与内存映射设备进行交互。
以下是我将如何处理私有成员变量,并在评论中说明理由:
T volatile buffer_[depth]; // will never touch buffer_[] via array handle,
// but don't want compiler to optimize it out;
// and technically, the elements are volatile due to push()
T volatile* const begin_; // buffer_[] has elements of type 'T volatile', so
// keep type of pointer consistent with what it points to
T volatile* const end_; // "
T volatile* volatile head_; // value must be volatile, as unknown ISR thread will touch;
// also, keep type of pointer consistent
// pointer should be volatile since ISRs will read outside
// of "main" thread context
T volatile* volatile tail_; // value should be volatile since multiple ISRs will touch;
// also, keep type of pointer consistent
// pointer should be volatile since multiple ISRs will touch
如果我使用std::array
而不是buffer_[]
,我不确定我将如何强制不仅数组的元素而且底层指针/迭代器也是易变的。 例如, std::array<T volatile, uint32_t depth> volatile
?
如果我将整个MpscQueue
对象设置为 volatile,我不确定我将如何强制“波动性”不仅会渗透到指针本身(即* volatile
),还会渗透到指向的值(即T volatile* volatile
而不仅仅是T* volatile
)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.