[英]Simple lock free stack c++11
我已經在 C++ 中看到了幾個過於復雜的(在我看來顯然)無鎖堆棧的實現(使用像這里的標簽),我想出了我認為是一個簡單但仍然有效的實現。 由於我在任何地方都找不到這個實現(我已經看到 Push 函數的實現與我所做的類似,但不是 Pop),我猜測它在某些方面是不正確的(最有可能在 ABA 案例中失敗):
template<typename Data>
struct Element
{
Data mData;
Element<Data>* mNext;
};
template<typename Data>
class Stack
{
public:
using Obj = Element<Data>;
std::atomic<Obj*> mHead;
void Push(Obj *newObj)
{
newObj->mNext = mHead.load();
//Should I be using std::memory_order_acq_rel below??
while(!mHead.compare_exchange_weak(newObj->mNext, newObj));
}
Obj* Pop()
{
Obj* old_head = mHead.load();
while (1)
{
if (old_head == nullptr)
return nullptr;
//Should I be using std::memory_order_acq_rel below??
if(mHead.compare_exchange_weak(old_head, old_head->mNext)) ///<<< CL1
return old_head;
}
}
};
我假設 Push 和 Pop 的調用者將負責內存分配和釋放。 另一種選擇是將上述 Push 和 Pop 方法設為私有方法,並擁有新的公共函數來處理內存分配並在內部調用這些函數。 我相信這個實現中最棘手的部分是我用“CL1”標記的那一行。 我認為它是正確的並且在 ABA 案例中仍然有效的原因如下:
可以說ABA案例確實發生了。 這意味着“CL1”處的 mHead 將等於 old_head,但它們指向的對象實際上與我將 mHead 分配給 old_head 時最初指向的對象不同。 但是,我認為即使它是一個不同的對象,我們仍然可以,因為我們知道它是一個有效的“頭”。 old_head 指向與 mHead 相同的對象,因此它是堆棧的有效頭,這意味着 old_head->mNext 是有效的下一個頭。 所以,將 mHead 更新為 old_head->mNext 仍然是正確的!
總結一下:
那么,我的實現有效嗎? 我錯過了什么?
一般來說,如果數據類型是按位正則的,那么實現可能是正確的,如果只是半正則(或相等不是按位),則不可避免地會受到 ABA 的影響。
如果數據類型是按位規則的,則 CAS 操作就足夠了,例如對於整數。 ABA 不是問題,因為 A 等於 A,故事結束。
Harris 算法似乎允許非按位正則類型提供集合實現,但以無界性能為代價,更糟糕的是,它回避了如何分配列表節點的問題。 這意味着要解決的核心問題是提供一個 O(1) 無鎖分配器,而這樣的數據結構是不存在的,因為所涉及的指針只是半規則的。 特別是,當指針僅將字節數組標識為內存塊時,它是規則的,當它標識鏈表的節點時,它不再是規則的,因為指針相等並不能確保嵌入的下一個指針相等。
沒有可能的解決方法。 我懷疑 DCAS 會有所幫助,我也懷疑將恆定時間限制放寬到線性是否有效。
對於不可搶占的線程,只要它保護的臨界區是有界時間,自旋鎖就可以工作。 您可以在 MacOS 和 RT-Linux 上獲得這些。
如果你需要跨平台,這個lfstack可以做跨平台構建,它是c native內置的
例子:-
int* int_data;
lfstack_t mystack;
if (lfstack_init(&mystack) == -1)
return -1;
/** Wrap This scope in other threads **/
int_data = (int*) malloc(sizeof(int));
assert(int_data != NULL);
*int_data = i++;
/*PUSH*/
while (lfstack_push(&mystack, int_data) == -1) {
printf("ENQ Full ?\n");
}
/** Wrap This scope in other threads **/
/*POP*/
while ( (int_data = lfstack_pop(&mystack)) == NULL) {
printf("POP EMPTY ..\n");
}
// printf("%d\n", *(int*) int_data );
free(int_data);
/** End **/
lfstack_destroy(&mystack);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.