我需要为无锁定堆栈编写void push(const T& val)
实现。 问题是,compare_exchange_weak期望非原子node*
,但我必须使用std::atomic<node*> next
字段而不是常规node* next
。 我试图这样做C++如何锁定自由堆栈推入原子
void push(const T& val) {
node* new_node = new node(val);
node* local_next = new_node->next.load();
while (!head.compare_exchange_weak(local_next, new_node));
}
但如果创造让local_next
事情更糟,解决这个问题。我测试了两种代码。第一个有非原子字段node* next
,我在下面的测试代码中丢失了大约20-30个元素。而使用第二个变体,我陷入了僵局。
测试代码:
#include <iostream>
#include <thread>
#include <atomic>
#include "lock_free_stack.h"
using namespace std;
void test(lock_free_stack<int>& st, atomic<int>& sum) {
st.push(1);
shared_ptr<int> val(st.pop());
while (val == nullptr) { }
sum.store(sum.load() + *val);
}
int main(int argc, const char * argv[]) {
atomic<int> sum;
sum.store(0);
for (int i = 0; i < 100; ++i) {
lock_free_stack<int> st;
thread t1(test, ref(st), ref(sum));
thread t2(test, ref(st), ref(sum));
thread t3(test, ref(st), ref(sum));
thread t4(test, ref(st), ref(sum));
thread t5(test, ref(st), ref(sum));
thread t6(test, ref(st), ref(sum));
thread t7(test, ref(st), ref(sum));
thread t8(test, ref(st), ref(sum));
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
t6.join();
t7.join();
t8.join();
}
if (sum.load() == 800) {
cout << "CORRECT" << endl;
} else {
cout << "TIME TO REWRITE STACK " << sum << endl;
}
return 0;
}
而且我的无锁堆栈的代码(第一变体):
#ifndef lock_free_stack_hard_lock_free_stack_h
#define lock_free_stack_hard_lock_free_stack_h
template <typename T>
class lock_free_stack {
private:
struct node {
node* next;
std::shared_ptr<T> value;
node (const T& val) : value(std::make_shared<T>(val)) { }
};
std::atomic<node*> head;
std::shared_ptr<T> default_value;
public:
lock_free_stack() : head(nullptr), default_value(std::make_shared<T>()) { }
void push(const T& val) {
node* new_node = new node(val);
new_node->next = head.load();
while (!head.compare_exchange_weak(new_node->next, new_node));
}
std::shared_ptr<T> pop() {
node* old_head = head.load();
while (old_head && !head.compare_exchange_weak(old_head, old_head->next));
if (old_head) {
return old_head->value;
} else {
return std::shared_ptr<T>();
}
}
};
#endif
而第二个变型:
#ifndef lock_free_stack_hard_lock_free_stack_h
#define lock_free_stack_hard_lock_free_stack_h
template <typename T>
class lock_free_stack {
private:
struct node {
std::atomic<node*> next;
std::shared_ptr<T> value;
node (const T& val) : value(std::make_shared<T>(val)) { }
};
std::atomic<node*> head;
std::shared_ptr<T> default_value;
public:
lock_free_stack() : head(nullptr), default_value(std::make_shared<T>()) { }
void push(const T& val) {
node* new_node = new node(val);
new_node->next = head.load();
node* local_next = new_node->next.load();
while (!head.compare_exchange_weak(local_next, new_node));
}
std::shared_ptr<T> pop() {
node* old_head = head.load();
while (old_head && !head.compare_exchange_weak(old_head, old_head->next));
if (old_head) {
return old_head->value;
} else {
return std::shared_ptr<T>();
}
}
};
#endif
所以最终的问题是如何正确创建local_next
?
谢谢。与测试
['compare_exchange_weak'](http://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange)的例子实际上实现了一个无锁'stack :: push'。 – Barry
@Barry,字段'node * next'实际上应该看起来像'std :: atomic next' –
Maksim
你可能会碰到一个编译器错误:[msvc](https://connect.microsoft.com/VisualStudio/feedback/details/819819/std-atomic-compare-exchange-weak-has-spurious-write-which-cause-race-conditions),[clang](http://lists.cs.uiuc.edu/pipermail/llvmbugs/ 2014-February/032833.html)和[gcc](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60272)在编译compare_exchange_ *时都遇到了问题。请参阅cppreference.com上的[示例](http://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange)。 – programmerjake