无锁并发相关-无锁栈,无锁队列

无锁栈:

#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

posted @ 2024-01-30 10:42  白伟碧一些小心得  阅读(21)  评论(0编辑  收藏  举报