无锁栈的几种实现方式

以下几种方式
● stack1 使用了最简单的 threads_in_pop 记录当前 pop 的线程数,控制并发访问安全
● stack2 使用了风险指针,对指涉指针进行保护
● stack3 使用 c++20的原子智能指针,减少了对内存的管理,十分简洁
● stack4 使用了两层内外指针,分别标志使用数和完成数,解决了 ABA 问题。

#include <atomic>
#include <functional>
#include <memory>
#include <thread>

template <typename T>
class stack_lock_free {
private:
    struct node {
        std::shared_ptr<T> data;
        node* next;
        node(T const& value)
            : data(std::make_shared<T>(value))
            , next(nullptr)
        {
        }
    };

    std::atomic<node*> head;
    std::atomic<node*> to_be_deleted;
    std::atomic<unsigned> threads_in_pop;

    static void delete_nodes(node* nodes)
    {
        while (nodes) {
            node* next = nodes->next;
            delete nodes;
            nodes = next;
        }
    }

    void try_reclain(node* old_head)
    {
        if (threads_in_pop == 1) {
            node* nodes_to_delete = to_be_deleted.exchange(nullptr);
            if (!--threads_in_pop) {
                delete_nodes(nodes_to_delete);
            } else if (nodes_to_delete) {
                chain_pending_nodes(nodes_to_delete);
            }
            delete old_head;
        } else {
            chain_pending_node(old_head);
            --threads_in_pop;
        }
    }

    void chain_pending_nodes(node* nodes)
    {
        node* last = nodes;
        while (last->next) {
            last = last->next;
        }
        chain_pending_nodes(nodes, last);
    }

    void chain_pending_nodes(node* first, node* last)
    {
        last->next = to_be_deleted.load();
        while (!to_be_deleted.compare_exchange_weak(last->next, first))
            ;
    }

    void chain_pending_node(node* node)
    {
        chain_pending_nodes(node, node);
    }

public:
    stack_lock_free() { }
    ~stack_lock_free()
    {
        while (pop()) { }
        node* to_delete = to_be_deleted.exchange(nullptr);
        delete_nodes(to_delete);
    }

    std::shared_ptr<T> pop()
    {
        ++threads_in_pop;
        node* old_head = head.load();
        while (old_head && !head.compare_exchange_weak(old_head, old_head->next))
            ;
        std::shared_ptr<T> res;
        if (old_head) {
            res.swap(old_head->data);
        }
        try_reclain(old_head);
        return res;
    }

    void push(T const& value)
    {
        node* new_node = new node(value);
        new_node->next = head.load();
        while (!head.compare_exchange_weak(new_node->next, new_node))
            ;
    }
};

/* Hazard pointers implementation */

unsigned const max_hazard_pointers = 100;
struct hazard_pointer {
    std::atomic<void*> pointer;
    std::atomic<std::thread::id> id;
};

inline hazard_pointer hazard_pointers[max_hazard_pointers];

class hp_owner {
private:
    hazard_pointer* hp;

public:
    hp_owner(hp_owner const&) = delete;
    hp_owner& operator=(hp_owner const&) = delete;

    hp_owner()
        : hp(nullptr)
    {
        for (unsigned i = 0; i < max_hazard_pointers; ++i) {
            std::thread::id old_id;
            if (hazard_pointers[i].id.compare_exchange_strong(
                    old_id, std::this_thread::get_id())) {
                hp = &hazard_pointers[i];
                break;
            }
        }
        if (!hp) {
            throw std::runtime_error("No hazard pointers available");
        }
    }

    std::atomic<void*>& get_pointer()
    {
        return hp->pointer;
    }

    ~hp_owner()
    {
        hp->pointer.store(nullptr);
        hp->id.store(std::thread::id());
    }
};

inline std::atomic<void*>& get_hazard_pointer_for_current_thread()
{
    thread_local static hp_owner hazard;
    return hazard.get_pointer();
}

inline bool outstanding_hazard_pointers_for(void* p)
{
    for (unsigned i = 0; i < max_hazard_pointers; ++i) {
        if (hazard_pointers[i].pointer.load() == p) {
            return true;
        }
    }
    return false;
}

template <typename T>
void do_delete(void* p)
{
    delete static_cast<T*>(p);
}

struct data_to_reclaim {
    void* data;
    std::function<void(void*)> deleter;
    data_to_reclaim* next;
    template <typename T>
    data_to_reclaim(T* p)
        : data(p)
        , deleter(&do_delete<T>)
        , next(nullptr)
    {
    }
    ~data_to_reclaim()
    {
        deleter(data);
    }
};

inline std::atomic<data_to_reclaim*> nodes_to_reclaim;

inline void add_to_reclaim_list(data_to_reclaim* node)
{
    node->next = nodes_to_reclaim.load();
    while (!nodes_to_reclaim.compare_exchange_weak(node->next, node))
        ;
}
template <typename T>
inline void reclaim_later(T* data)
{
    add_to_reclaim_list(new data_to_reclaim(data));
}

inline void delete_nodes_with_no_hazards()
{
    data_to_reclaim* current = nodes_to_reclaim.exchange(nullptr);
    while (current) {
        data_to_reclaim* const next = current->next;
        if (!outstanding_hazard_pointers_for(current->data)) {
            delete current;
        } else {
            add_to_reclaim_list(current);
        }
        current = next;
    }
}

template <typename T>
class stack_lock_free2 {
private:
    struct node {
        std::shared_ptr<T> data;
        node* next;
        node(T const& value)
            : data(std::make_shared<T>(value))
            , next(nullptr)
        {
        }
    };

    std::atomic<node*> head;
    std::atomic<node*> to_be_deleted;

    static void delete_nodes(node* nodes)
    {
        while (nodes) {
            node* next = nodes->next;
            delete nodes;
            nodes = next;
        }
    }

public:
    stack_lock_free2() { }
    ~stack_lock_free2()
    {
        while (pop()) { }
        node* to_delete = to_be_deleted.exchange(nullptr);
        delete_nodes(to_delete);
    }

    std::shared_ptr<T> pop()
    {
        std::atomic<void*>& hp = get_hazard_pointer_for_current_thread();
        node* old_head = head.load();
        do {
            node* temp;
            do {
                temp = old_head;
                hp.store(old_head);
                old_head = head.load();
            } while (old_head != temp);

        } while (old_head && !head.compare_exchange_strong(old_head, old_head->next));
        hp.store(nullptr);
        std::shared_ptr<T> res;
        if (old_head) {
            res.swap(old_head->data);
            if (outstanding_hazard_pointers_for(old_head)) {
                reclaim_later(old_head);
            } else {
                delete old_head;
            }
            delete_nodes_with_no_hazards();
        }
        return res;
    }

    void push(T const& value)
    {
        node* new_node = new node(value);
        new_node->next = head.load();
        while (!head.compare_exchange_weak(new_node->next, new_node))
            ;
    }
};

/* Lock-free stack implementation of reference counted objects */
template <typename T>
class stack_lock_free3 {
private:
    struct node {
        std::shared_ptr<T> data;
        std::atomic<std::shared_ptr<node>> next;
        node(T const& value)
            : data(std::make_shared<T>(value))
            , next(nullptr)
        {
        }
    };

public:
    std::atomic<std::shared_ptr<node>> head;
    void push(T const& value)
    {
        std::shared_ptr<node> const new_node = std::make_shared<node>(value);
        new_node->next.store(head.load());
        std::shared_ptr<node> excepted = new_node->next.load();
        while (!head.compare_exchange_weak(excepted, new_node))
            ;
    }
    std::shared_ptr<T> pop()
    {
        std::shared_ptr<node> old_head = head.load();
        while (old_head && !head.compare_exchange_weak(old_head, old_head->next.load()))
            ;
        return old_head ? old_head->data : std::shared_ptr<T>();
    }
    ~stack_lock_free3()
    {
        while (pop()) { }
    }
};

/* Lock-free stack implementation of internal and external references */
template <typename T>
class stack_lock_free4 {
private:
    struct node;
    struct counted_node_ptr {
        int external_count;
        node* ptr;
    };
    struct node {
        std::atomic<int> internal_count;
        std::shared_ptr<T> data;
        std::shared_ptr<counted_node_ptr> next;
        node(T const& value)
            : internal_count(0)
            , data(std::make_shared<T>(value))
        {
        }
    };

    std::atomic<std::shared_ptr<counted_node_ptr>> head;

public:
    ~stack_lock_free4()
    {
        while (pop()) { }
    };
    stack_lock_free4()
    {
        head.store(std::make_shared<counted_node_ptr>());
    }

    void push(T const& value)
    {
        std::shared_ptr<counted_node_ptr> new_node = std::make_shared<counted_node_ptr>();
        new_node->ptr = new node(value);
        new_node->external_count = 1;
        new_node->ptr->next = head.load();
        while (!head.compare_exchange_weak(new_node->ptr->next, new_node))
            ;
    }

    std::shared_ptr<T> pop()
    {
        std::shared_ptr<counted_node_ptr> old_head = head.load();
        for (;;) {
            increase_head_count(old_head);
            node* const ptr = old_head->ptr;
            if (!ptr) {
                return std::shared_ptr<T>();
            }
            if (head.compare_exchange_strong(old_head, ptr->next)) {
                std::shared_ptr<T> res;
                res.swap(ptr->data);
                int const count_increase = old_head->external_count - 2;
                if (ptr->internal_count.fetch_add(count_increase) == -count_increase) {
                    delete ptr;
                }
                return res;
            } else if (ptr->internal_count.fetch_sub(1) == 1) {
                delete ptr;
            }
        }
    }

    void increase_head_count(std::shared_ptr<counted_node_ptr>& old_counter)
    {
        std::shared_ptr<counted_node_ptr> new_counter = std::make_shared<counted_node_ptr>();
        do {
            new_counter = old_counter;
            ++(new_counter->external_count);
        } while (!head.compare_exchange_strong(old_counter, new_counter));
        old_counter->external_count = new_counter->external_count;
    }
};
posted @ 2026-02-08 15:06  大胖熊哈  阅读(18)  评论(0)    收藏  举报