无锁并发相关-无锁栈,无锁队列
无锁栈:
#pragma once template<typename T> class ref_count_stack { private: //前置声明节点类型 struct count_node; struct counted_node_ptr { //1 外部引用计数 int external_count; //2 节点地址 count_node* ptr; }; struct count_node { //3 数据域智能指针 std::shared_ptr<T> data; //4 节点内部引用计数 std::atomic<int> internal_count; //5 下一个节点 counted_node_ptr next; count_node(T const& data_): data(std::make_shared<T>(data_)), internal_count(0) {} }; //6 头部节点 std::atomic<counted_node_ptr> head; public: //增加头部节点引用数量 void increase_head_count(counted_node_ptr& old_counter) { counted_node_ptr new_counter; do { new_counter = old_counter; ++new_counter.external_count; }//7 循环判断保证head和old_counter想等时做更新,多线程情况保证引用计数原子递增。 while (!head.compare_exchange_strong(old_counter, new_counter, std::memory_order_acquire, std::memory_order_relaxed)); //8 走到此处说明head的external_count已经被更新了 old_counter.external_count = new_counter.external_count; } std::shared_ptr<T> pop() { counted_node_ptr old_head = head.load(); for (;;) { increase_head_count(old_head); count_node* const ptr = old_head.ptr; //1 判断为空责直接返回 if (!ptr) { return std::shared_ptr<T>(); } //2 本线程如果抢先完成head的更新 if (head.compare_exchange_strong(old_head, ptr->next, std::memory_order_relaxed)) { //返回头部数据 std::shared_ptr<T> res; //交换数据 res.swap(ptr->data); //3 减少外部引用计数,先统计到目前为止增加了多少外部引用 int const count_increase = old_head.external_count - 2; //4 将内部引用计数添加 if (ptr->internal_count.fetch_add(count_increase, std::memory_order_release) == -count_increase) { delete ptr; } return res; } else if (ptr->internal_count.fetch_add(-1, std::memory_order_acquire) == 1) { //5 //如果当前线程操作的head节点已经被别的线程更新,则减少内部引用计数 //当前线程减少内部引用计数,返回之前值为1说明指针仅被当前线程引用 ptr->internal_count.load(std::memory_order_acquire); delete ptr; } } } ref_count_stack(){ counted_node_ptr head_node_ptr; //头节点开始只做标识用,没效果 head_node_ptr.external_count = 0; head_node_ptr.ptr = nullptr; head.store(head_node_ptr); } ~ref_count_stack() { //循环出栈 while (pop()); } void push(T const& data) { counted_node_ptr new_node; new_node.ptr = new count_node(data); new_node.external_count = 1; new_node.ptr->next = head.load(); while (!head.compare_exchange_weak(new_node.ptr->next, new_node, std::memory_order_release, std::memory_order_relaxed)); } };
验证:
void TestRefCountStack() { ref_count_stack<int> ref_count_stack; std::set<int> rmv_set; std::mutex set_mtx; std::thread t1([&]() { for (int i = 0; i < 20000; i++) { ref_count_stack.push(i); std::cout << "push data " << i << " success!" << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(5)); } }); std::thread t2([&]() { for (int i = 0; i < 10000;) { auto head = ref_count_stack.pop(); if (!head) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); continue; } std::lock_guard<std::mutex> lock(set_mtx); rmv_set.insert(*head); std::cout << "pop data " << *head << " success!" << std::endl; i++; } }); std::thread t3([&]() { for (int i = 0; i < 10000;) { auto head = ref_count_stack.pop(); if (!head) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); continue; } std::lock_guard<std::mutex> lock(set_mtx); rmv_set.insert(*head); std::cout << "pop data " << *head << " success!" << std::endl; i++; } }); t1.join(); t2.join(); t3.join(); assert(rmv_set.size() == 20000); }
详细见:https://gitee.com/secondtonone1/boostasio-learn/tree/master/concurrent/day17-LockFreeStack
https://llfc.club/category?catid=225RaiVNI8pFDD5L4m807g7ZwmF#!aid/2aLIpla1ZE4k23o0p0hSxXabSXC
无锁队列:
#pragma once #include<atomic> #include<memory> #include <cassert> template<typename T> class lock_free_queue { private: struct node_counter { unsigned internal_count : 30; //⇽--- 2 unsigned external_counters : 2; }; struct node; struct counted_node_ptr { //存在破坏trivial class 的风险 /*bool operator == (const counted_node_ptr& cnp) { return (external_count == cnp.external_count && ptr == cnp.ptr); }*/ //构造初始化各成员 counted_node_ptr():external_count(0), ptr(nullptr) {} int external_count; node* ptr; }; struct node { std::atomic<T*> data; std::atomic<node_counter> count; //⇽--- 1 std::atomic<counted_node_ptr> next; node(int external_count = 2) { node_counter new_count; new_count.internal_count = 0; //⇽--- 4 new_count.external_counters = external_count; count.store(new_count); counted_node_ptr node_ptr; node_ptr.ptr = nullptr; node_ptr.external_count = 0; next.store(node_ptr); } void release_ref() { std::cout << "call release ref " << std::endl; node_counter old_counter = count.load(std::memory_order_relaxed); node_counter new_counter; do { new_counter = old_counter; //1 --new_counter.internal_count; } //2 while (!count.compare_exchange_strong( old_counter, new_counter, std::memory_order_acquire, std::memory_order_relaxed)); if (!new_counter.internal_count && !new_counter.external_counters) { //3 delete this; std::cout << "release_ref delete success" << std::endl; destruct_count.fetch_add(1); } } }; std::atomic<counted_node_ptr> head; //⇽--- 1 std::atomic<counted_node_ptr> tail; // ⇽--- 1 void set_new_tail(counted_node_ptr& old_tail, counted_node_ptr const& new_tail) { node* const current_tail_ptr = old_tail.ptr; // ⇽--- 2 此处仅有一个线程能设置tail为new_tail,失败的会更新old_tail为tail的新值 // 为防止失败的线程重试导致tail被再次更新所以添加了后面的&&判断 while (!tail.compare_exchange_weak(old_tail, new_tail) && old_tail.ptr == current_tail_ptr); // ⇽--- 3 if (old_tail.ptr == current_tail_ptr) //⇽--- 4 free_external_counter(old_tail); else //⇽--- 5 current_tail_ptr->release_ref(); } static void free_external_counter(counted_node_ptr& old_node_ptr) { std::cout << "call free_external_counter " << std::endl; node* const ptr = old_node_ptr.ptr; int const count_increase = old_node_ptr.external_count - 2; node_counter old_counter = ptr->count.load(std::memory_order_relaxed); node_counter new_counter; do { new_counter = old_counter; //⇽--- 1 --new_counter.external_counters; //⇽--- 2 new_counter.internal_count += count_increase; } //⇽--- 3 while (!ptr->count.compare_exchange_strong( old_counter, new_counter, std::memory_order_acquire, std::memory_order_relaxed)); if (!new_counter.internal_count && !new_counter.external_counters) { //⇽--- 4 destruct_count.fetch_add(1); std::cout << "free_external_counter delete success" << std::endl; delete ptr; } } static void increase_external_count( std::atomic<counted_node_ptr>& counter, counted_node_ptr& old_counter) { counted_node_ptr new_counter; do { new_counter = old_counter; ++new_counter.external_count; } while (!counter.compare_exchange_strong( old_counter, new_counter, std::memory_order_acquire, std::memory_order_relaxed)); old_counter.external_count = new_counter.external_count; } public: lock_free_queue() { counted_node_ptr new_next; new_next.ptr = new node(); new_next.external_count = 1; tail.store(new_next); head.store(new_next); std::cout << "new_next.ptr is " << new_next.ptr << std::endl; } ~lock_free_queue() { while (pop()); auto head_counted_node = head.load(); delete head_counted_node.ptr; } void push(T new_value) { std::unique_ptr<T> new_data(new T(new_value)); counted_node_ptr new_next; new_next.ptr = new node; new_next.external_count = 1; counted_node_ptr old_tail = tail.load(); for (;;) { increase_external_count(tail, old_tail); T* old_data = nullptr; //⇽--- 6 if (old_tail.ptr->data.compare_exchange_strong( old_data, new_data.get())) { counted_node_ptr old_next; counted_node_ptr now_next = old_tail.ptr->next.load(); //⇽--- 7 if (!old_tail.ptr->next.compare_exchange_strong( old_next, new_next)) { //⇽--- 8 delete new_next.ptr; new_next = old_next; // ⇽--- 9 } set_new_tail(old_tail, new_next); new_data.release(); break; } else // ⇽--- 10 { counted_node_ptr old_next ; // ⇽--- 11 if (old_tail.ptr->next.compare_exchange_strong( old_next, new_next)) { // ⇽--- 12 old_next = new_next; // ⇽--- 13 new_next.ptr = new node; } // ⇽--- 14 set_new_tail(old_tail, old_next); } } } std::unique_ptr<T> pop() { counted_node_ptr old_head = head.load(std::memory_order_relaxed); for (;;) { increase_external_count(head, old_head); node* const ptr = old_head.ptr; if (ptr == tail.load().ptr) { //头尾相等说明队列为空,要减少内部引用计数 ptr->release_ref(); return std::unique_ptr<T>(); } // ⇽--- 2 counted_node_ptr next = ptr->next.load(); if (head.compare_exchange_strong(old_head, next)) { T* const res = ptr->data.exchange(nullptr); free_external_counter(old_head); return std::unique_ptr<T>(res); } ptr->release_ref(); } } static std::atomic<int> destruct_count; }; template<typename T> std::atomic<int> lock_free_queue<T>::destruct_count = 0;
测试:
void TestLockFreeQueMultiPushPop() { lock_free_queue<int> que; std::thread t1([&]() { for (int i = 0; i < TESTCOUNT * 100; i++) { que.push(i); std::cout << "push data is " << i << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(10)); } }); std::thread t4([&]() { for (int i = TESTCOUNT*100; i < TESTCOUNT * 200; i++) { que.push(i); std::cout << "push data is " << i << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(10)); } }); std::thread t2([&]() { for (int i = 0; i < TESTCOUNT * 100;) { auto p = que.pop(); if (p == nullptr) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); continue; } i++; std::cout << "pop data is " << *p << std::endl; } }); std::thread t3([&]() { for (int i = 0; i < TESTCOUNT * 100;) { auto p = que.pop(); if (p == nullptr) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); continue; } i++; std::cout << "pop data is " << *p << std::endl; } }); t1.join(); t2.join(); t3.join(); t4.join(); assert(que.destruct_count == TESTCOUNT * 200); }
https://llfc.club/category?catid=225RaiVNI8pFDD5L4m807g7ZwmF#!aid/2ahHZgMOCIfy9TnBOaS8pXKjf5n