[英]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.