易失成员变量与易失对象? [英] Volatile member variables vs. volatile object?
问题描述
我正在尝试在下面的 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屋!