2022-10-11 C++并发编程(三十三)

C++并发编程, 无锁并发队列


老林的C语言新课, 想快速入门点此 <C 语言编程核心突破>



前言

前几期文章我们介绍了无锁并发栈的实现,接下来,再进一步,编写无锁队列。

无锁数据结构说实在的,给你一个现成的实现,都不一定能看懂,更别说推断漏洞啥的,绝对是个费脑子的活。

但有时候不得不去啃这块骨头,俗称造轮子,通过软磨硬泡,还是有办法搞懂的。


一、根据内外计数实现的无锁并发队列

队列是最基础简单的数据结构, 简单到只有两个接口, 入队 push 以及出队 pop, 然而一旦涉及到多线程, 尤其是无锁的多线程并发, 就比较难了.

并非是我要劝退各位, 只不过, 以下代码我自己都不能说完全明白, 实现不完美, 还有改进的可能.

无锁队列相较于无锁栈的难点是, 这是一个一进一出两个原子操作同时进行, 为了满足这种需求, 需要设计复杂的节点结构, 不仅要有内外两个引用计数, 甚至为了实现正确的计数, 我们需要再增加一个外部计数器的计数器.

如果把我们的大脑比喻为计算机, 那么短时记忆的 catch 实在太小, 不足以理解节点结构, 需要用内存辅助进行信息传递, 所以我给各位一张节点结构图:

队列节点结构

初看像个拿着一根冰棍的小人, 我则更倾向于一个氨基酸分子. 队列就是这样的小人, 一个个的左手拉右手.

算法的所有步骤都是基于这张图, 不照着图看, 我没信心看下去. 而具体的细节, 除了看代码, 还需要一步一步的 debug.

原理不难, 还是通过引用计数控制内存回收, 同时有多个线程一同访问同一节点, 那么每个线程的外部计数都会同时增加并同步,

通过 CAS 比较替换, 如果成功交换, 操作完成, 离开节点, 则会将引用计数更新, 方法是将外部计数减去 2, 并入内部计数中, 同时减少一个外部计数的计数器 ( 意味着成功的 push 了一个元素, 或者成功 pop 了一个节点 ).

如果 CAS 比较失败, 则直接在内部计数减 1, 最终内部计数为 0, 外部计数的计数器也为0, 就可以将资源删除.

#include <atomic>
#include <iostream>
#include <memory>
#include <thread>

namespace noLock
{

// 无锁队列类
template <typename T>
struct lockFreeQueue;

// 节点计数指针,
// 含有一个 int exCnt 外部计数,
// 以及一个 node<T>* nodePtr
template <typename T>
struct cntNodePtr;

// 节点类,
// 含有一个数据的原子指针 atomic<T*> dataPtr,
// 一个原子节点计数 atomic<nodeCounter> nodeCnter,
// 节点计数指针 cntNodePtr<T> next
template <typename T>
struct node;

// 节点计数,
// 含有一个内部计数 unsigned intCnt,
// 一个外部计数的计数 unsigned exCnters
struct nodeCounter
{
    // 内部计数
    int intCnt : 30;
    // 外部计数的计数
    unsigned exCnters : 2;
};

template <typename T>
struct cntNodePtr
{
    // 外部计数
    std::int64_t exCnt = 0;
    // 节点指针
    node<T> *nodePtr = nullptr;
};

template <typename T>
struct node
{
    // 构造函数,
    // cntNodePtr<T> next 的 node<T>* nodePtr 指针置空, 内部计数置零
    // atomic<nodeCounter> nodeCnter 节点计数, 内部计数置零,外部计数的计数设为2
    // atomic<T *> dataPtr 数据原子指针置空
    node()
    {
        // 新节点计数
        nodeCounter newCnter;
        // 内部计数置零
        newCnter.intCnt = 0;
        // 外部计数的计数为2
        newCnter.exCnters = 2;

        // atomic<nodeCounter> nodeCnter 节点计数原子存储值
        nodeCnter.store(newCnter);

        // next 计数节点的 node<T>* nodePtr 指针置空
        next.nodePtr = nullptr;
        // next 计数节点的 int exCnt 指针外部计数置零
        next.exCnt = 0;
    }

    // 释放节点引用,
    // node 节点的节点计数的内部计数 intCnt 减 1,
    // 如果 node 节点的节点计数的内部计数 intCnt 为 0,
    // 且 node 节点的节点计数器的外部计数的计数也为 0,
    // 则删除 node 节点 this 资源
    void releaseRefNode()
    {
        // 获取原节点计数
        nodeCounter oldCounter = nodeCnter.load();

        // 新建节点计数
        nodeCounter newCounter;

        // 循环, 将原节点计数赋值给 newCounter, newCounter 内部计数减一
        // 比较替换,若节点计数 nodeCnter 等于 oldCounter, 更新为 newCounter
        // 否则更新 oldCounter, 继续循环
        do
        {
            newCounter = oldCounter;
            --newCounter.intCnt;
        } while (!nodeCnter.compare_exchange_strong(oldCounter, newCounter,
                                                    std::memory_order_acquire,
                                                    std::memory_order_relaxed));
        // 若 newCounter 的内部计数为 0, 外部计数的计数为 0, 则销毁此节点
        if ((newCounter.intCnt == 0U) && (newCounter.exCnters == 0U))
        {
            delete this;
        }
    }

  private:
    // 节点数据
    std::atomic<T *> dataPtr = nullptr;
    // 节点计数
    std::atomic<nodeCounter> nodeCnter;
    // 计数节点 next 指针
    cntNodePtr<T> next;

    friend struct lockFreeQueue<T>;
};

template <typename T>
struct lockFreeQueue
{
    // 构造函数,新建头节点指针,尾节点指针指向头节点
    lockFreeQueue()
    {
        // 新建计数节点指针
        cntNodePtr<T> HeadCntNodePtr;

        // 构建计数节点指针的 node 节点资源
        HeadCntNodePtr.nodePtr = new node<T>;

        HeadCntNodePtr.exCnt = 1;

        // 将计数节点指针存入 head
        head.store(HeadCntNodePtr);

        // 将计数节点指针存入 tail
        tail.store(HeadCntNodePtr);
    }

    // 不可拷贝构造
    lockFreeQueue(const lockFreeQueue &rhs) = delete;

    // 不可拷贝赋值
    auto operator=(const lockFreeQueue &rhs) -> lockFreeQueue & = delete;

    // 析构
    ~lockFreeQueue()
    {
        node<T> *nodePtr = head.load().nodePtr;
        while (nodePtr)
        {
            head.store(nodePtr->next);
            delete nodePtr->dataPtr.load();
            delete nodePtr;
            nodePtr = head.load().nodePtr;
        }
    }

    // 入队
    void push(T newValue)
    {
        // 智能指针包裹数据
        std::unique_ptr<T> newData(new T(newValue));

        // 创建下一个节点计数指针
        cntNodePtr<T> newNext;
        // 将节点计数指针的节点指针分配节点资源
        newNext.nodePtr = new node<T>;
        // 将节点计数指针的外部计数设为 1
        newNext.exCnt = 1;

        // 获取尾部节点计数指针
        cntNodePtr<T> oldTail = tail.load();

        // 进入循环
        while (true)
        {
            // 引用尾部节点, 并更新外部计数
            increaseExternalCount(tail, oldTail);

            // 新建空数据指针
            T *oldData = nullptr;

            // 如果引用的节点指针其节点指针指向的数据指针为空, 则将其更新
            // 否则更新引用节点计数指针
            if (oldTail.nodePtr->dataPtr.compare_exchange_strong(oldData,
                                                                 newData.get()))
            {
                // 更新引用计数节点指针的节点指针的 next 节点
                oldTail.nodePtr->next = newNext;

                // tail 节点计数指针更新为 newNext 节点计数指针
                // oldTail 更新为原 tail 节点计数指针
                oldTail = tail.exchange(newNext);

                // 释放节点外部引用计数
                freeExternalCounter(oldTail);

                // newData 智能指针放弃对资源的占有
                oldData = newData.release();

                break;
            }

            // 释放引用节点, 节点的节点计数的内部计数减一
            oldTail.nodePtr->releaseRefNode();
        }
    }

    // 出队
    auto pop() -> std::unique_ptr<T>
    {
        // 获取原节点计数指针
        cntNodePtr<T> oldHead = head.load();
        // 循环
        while (true)
        {
            // 引用 head 节点计数指针, 并增加其外部引用计数 exCnt
            increaseExternalCount(head, oldHead);

            // 获取原节点计数指针的 nodePtr
            node<T> *const nodePtr = oldHead.nodePtr;

            // 若原节点计数指针的 nodePtr 等于 tail 的 nodePtr
            if (nodePtr == tail.load().nodePtr)
            {
                // 则释放节点引用, node 节点的节点计数的内部计数 intCnt 减 1,
                nodePtr->releaseRefNode();
                // 返回空值
                return std::unique_ptr<T>();
            }
            // 如果原节点计数指针等于 head ,
            // 曾更新 head 为下一个节点计数指针 next
            if (head.compare_exchange_strong(oldHead, nodePtr->next))
            {
                T *const res = nodePtr->dataPtr.exchange(nullptr);
                // 释放节点计数指针的节点的外部计数的计数 exCnters,
                // 增加节点计数指针的节点的内部计数 intCnt, 增加 exCnt - 2
                freeExternalCounter(oldHead);
                // 返回原节点计数指针持有的值
                return std::unique_ptr<T>(res);
            }

            // 则释放节点引用, node 节点的节点计数的内部计数 intCnt 减 1,
            nodePtr->releaseRefNode();
        }
    }

  private:
    // 从无锁队列中获取一个节点计数指针的引用, 并增加外部计数 exCnt
    static void increaseExternalCount(std::atomic<cntNodePtr<T>> &counter,
                                      cntNodePtr<T> &oldCounter)
    {
        // 新建节点计数指针
        cntNodePtr<T> newCounter;
        // 循环
        do
        {
            // 新节点计数指针等于原节点计数指针
            newCounter = oldCounter;
            // 增加新节点计数指针的外部计数
            ++newCounter.exCnt;
            // 当无锁队列中的节点计数指针等于原节点计数指针,
            // 用新节点计数指针更新它 否则更新原节点计数指针
        } while (!counter.compare_exchange_strong(oldCounter, newCounter,
                                                  std::memory_order_acquire,
                                                  std::memory_order_relaxed));
        // 更新原节点计数指针的外部计数
        oldCounter.exCnt = newCounter.exCnt;
    }

    // 释放节点计数指针的节点的外部计数的计数 exCnters -1,
    // 增加节点计数指针的节点的内部计数 intCnt, 增加 exCnt -2
    static void freeExternalCounter(cntNodePtr<T> &oldCntNodePtr)
    {
        // 新建节点指针, 指向原计数节点指针 oldCntNodePtr 的节点指针 nodePtr
        node<T> *const newNodePtr = oldCntNodePtr.nodePtr;

        // 计数增加值, 为原计数节点指针外部计数减二
        const int countIncrease = oldCntNodePtr.exCnt - 2;

        // 获取原节点计数
        nodeCounter oldCounter = newNodePtr->nodeCnter.load();

        // 新建节点计数
        nodeCounter newCounter;

        // 循环, 将原计数节点指针的外部计数转移到节点计数的内部计数
        do
        {
            // 将原节点计数赋值给新节点计数
            newCounter = oldCounter;

            // 新节点计数的外部计数的计数减一
            --newCounter.exCnters;

            // 新节点计数的内部计数增加 countIncrease
            newCounter.intCnt += countIncrease;

            // 当 newNodePtr 的节点计数等于原节点计数, 更新 newNodePtr 节点计数
            // 否则更新 oldCounter, 继续循环
        } while (!newNodePtr->nodeCnter.compare_exchange_strong(
            oldCounter, newCounter, std::memory_order_acquire,
            std::memory_order_relaxed));

        // 如果节点计数的内部计数和外部计数的计数都为 0,
        // 则删除 newNodePtr 指向的节点
        if ((newCounter.intCnt == 0U) && (newCounter.exCnters == 0U))
        {
            delete newNodePtr;
        }
    }

    // 头节点计数指针
    std::atomic<cntNodePtr<T>> head;
    // 尾节点计数指针
    std::atomic<cntNodePtr<T>> tail;
};

} // namespace noLock

auto main() -> int
{
    noLock::lockFreeQueue<int> lfs;

    // std::cout << lfs.isNoLock() << std::endl;

    std::thread t0([&lfs] {
        for (int i = 0; i != 1; ++i)
        {
            lfs.push(i);
        }
    });

    std::thread t1([&lfs] {
        for (int i = 0; i != 5; ++i)
        {
            if (lfs.pop())
            {
                std::cout << 1 << "\n";
            }
        }
    });

    //    std::thread t2([&lfs] {
    //        for (int i = 0; i != 500; ++i)
    //        {
    //            std::cout << *lfs.pop() << "\n";
    //        }
    //    });
    //
    //    std::thread t3([&lfs] {
    //        for (int i = 0; i != 500; ++i)
    //        {
    //            std::cout << *lfs.pop() << "\n";
    //        }
    //    });
    //
    //    std::thread t4([&lfs] {
    //        for (int i = 0; i != 500; ++i)
    //        {
    //            std::cout << *lfs.pop() << "\n";
    //        }
    //    });
    //
    //    std::thread t5([&lfs] {
    //        for (int i = 0; i != 500; ++i)
    //        {
    //            std::cout << *lfs.pop() << "\n";
    //        }
    //    });
    //
    //    std::thread t6([&lfs] {
    //        for (int i = 0; i != 500; ++i)
    //        {
    //            std::cout << *lfs.pop() << "\n";
    //        }
    //    });

    t0.join();
    t1.join();
    //    t2.join();
    //    t3.join();
    //    t4.join();
    //    t5.join();
    //    t6.join();
    lfs.push(1);
    std::cout << *lfs.pop() << std::endl;

    return 0;
}

以上实现中的 push 操作有个问题, 循环中如果 if 条件不成立, 会引起不必要的循环锁, 可以通过加一个条件分支进行优化, 协助将 tail 节点更新后, 再进行重新的循环推入数据.

#include <atomic>
#include <iostream>
#include <memory>
#include <thread>

namespace noLock
{

// 无锁队列类
template <typename T>
struct lockFreeQueue;

// 节点计数指针,
// 含有一个 int exCnt 外部计数,
// 以及一个 node<T>* nodePtr
template <typename T>
struct cntNodePtr;

// 节点类,
// 含有一个数据的原子指针 atomic<T*> dataPtr,
// 一个原子节点计数 atomic<nodeCounter> nodeCnter,
// 一个原子节点计数指针 atomic<cntNodePtr<T>> next
template <typename T>
struct node;

// 节点计数,
// 含有一个内部计数 int intCnt,
// 一个外部计数的计数 unsigned exCnters
struct nodeCounter
{
    // 内部计数
    int intCnt : 30;
    // 外部计数的计数
    unsigned exCnters : 2;
};

template <typename T>
struct cntNodePtr
{
    // 外部计数
    std::int64_t exCnt = 0;
    // 节点指针
    node<T> *nodePtr = nullptr;
};

template <typename T>
struct node
{
    // 构造函数,
    // atomic<cntNodePtr<T>> next 的 node<T>* nodePtr 指针置空, 外部计数置零
    // atomic<nodeCounter> nodeCnter 节点计数, 内部计数置零,外部计数的计数设为2
    // atomic<T *> dataPtr 数据原子指针置空
    node()
    {
        // atomic<nodeCounter> nodeCnter 节点计数,
        // 内部计数置零,外部计数的计数设为2
        nodeCnter.store(nodeCounter{0, 2});

        // std::atomic<cntNodePtr<T>> next 外部计数 exCnt 置零, nodePtr 指针置空
        next.store(cntNodePtr<T>());
    }

    // 释放节点引用,
    // node 节点的节点计数的内部计数 intCnt 减 1,
    // 如果 node 节点的节点计数的内部计数 intCnt 为 0,
    // 且 node 节点的节点计数器的外部计数的计数也为 0,
    // 则删除 node 节点 this 资源
    void releaseRefNode()
    {
        // 获取原节点计数
        nodeCounter oldCounter = nodeCnter.load();

        // 新建节点计数
        nodeCounter newCounter;

        // 循环, 将原节点计数赋值给 newCounter, newCounter 内部计数减一
        // 比较替换,若节点计数 nodeCnter 等于 oldCounter, 更新为 newCounter
        // 否则更新 oldCounter, 继续循环
        do
        {
            newCounter = oldCounter;
            --newCounter.intCnt;
        } while (!nodeCnter.compare_exchange_strong(oldCounter, newCounter,
                                                    std::memory_order_acquire,
                                                    std::memory_order_relaxed));
        // 若 newCounter 的内部计数为 0, 外部计数的计数为 0, 则销毁此节点
        if ((newCounter.intCnt == 0U) && (newCounter.exCnters == 0U))
        {
            delete this;
        }
    }

  private:
    // 节点数据
    std::atomic<T *> dataPtr = nullptr;
    // 节点计数
    std::atomic<nodeCounter> nodeCnter;
    // 原子化计数节点 next 指针
    std::atomic<cntNodePtr<T>> next;

    friend struct lockFreeQueue<T>;
};

template <typename T>
struct lockFreeQueue
{
    // 构造函数,新建头节点指针,尾节点指针指向头节点
    lockFreeQueue()
    {
        // 为 head 节点计数指针的节点分配资源, 外部引用设为1
        head.store(cntNodePtr<T>{1, new node<T>});

        // tail 指向head
        tail.store(head);
    }

    // 不可拷贝构造
    lockFreeQueue(const lockFreeQueue &rhs) = delete;

    // 不可拷贝赋值
    auto operator=(const lockFreeQueue &rhs) -> lockFreeQueue & = delete;

    // 析构
    ~lockFreeQueue()
    {
        node<T> *nodePtr = head.load().nodePtr;
        while (nodePtr)
        {
            head.store(nodePtr->next);
            delete nodePtr->dataPtr.load();
            delete nodePtr;
            nodePtr = head.load().nodePtr;
        }
    }

    // 入队
    void push(T newValue)
    {
        // 智能指针包裹数据
        std::unique_ptr<T> newData(new T(newValue));

        // 创建下一个节点计数指针
        cntNodePtr<T> newNext{1, new node<T>};

        // 获取尾部节点计数指针
        cntNodePtr<T> oldTail = tail.load();

        // 进入循环
        while (true)
        {
            // 引用尾部节点, 同步更新并增加外部计数
            increaseExternalCount(tail, oldTail);

            // 新建空数据指针
            T *oldData = nullptr;

            // 如果引用的节点指针其节点指针指向的数据指针为空, 则将其更新
            // 否则更新引用节点计数指针
            if (oldTail.nodePtr->dataPtr.compare_exchange_strong(oldData,
                                                                 newData.get()))
            {
                cntNodePtr<T> oldNext;
                // 如果原尾节点的节点的 next 节点不等于 oldNext, 则更新 oldNext,
                // 并删除 newNext 的节点资源, 并更新 newNext 为 oldNext
                if (!oldTail.nodePtr->next.compare_exchange_strong(oldNext,
                                                                   newNext))
                {
                    delete newNext.nodePtr;
                    newNext = oldNext;
                }

                setNewTail(oldTail, newNext);

                // newData 智能指针放弃对资源的占有
                oldData = newData.release();

                break;
            }

            // 新建空 oldNext 计数节点指针
            cntNodePtr<T> oldNext;

            // 如果原 tail 的节点的 next 节点为空, 则更新此 next 节点,
            // oldNext 等于 newNext, 分配新 node 节点资源给 newNext
            // 否则更新 oldNext
            if (oldTail.nodePtr->next.compare_exchange_strong(oldNext, newNext))
            {
                oldNext = newNext;
                newNext.nodePtr = new node<T>;
            }

            // 设置新尾节点
            setNewTail(oldTail, oldNext);
        }
    }

    // 出队
    auto pop() -> std::unique_ptr<T>
    {
        // 获取原节点计数指针
        cntNodePtr<T> oldHead = head.load();
        // 循环
        while (true)
        {
            // 引用 head 节点计数指针, 并增加其外部引用计数 exCnt
            increaseExternalCount(head, oldHead);

            // 获取原节点计数指针的 nodePtr
            node<T> *const nodePtr = oldHead.nodePtr;

            // 若原节点计数指针的 nodePtr 等于 tail 的 nodePtr
            if (nodePtr == tail.load().nodePtr)
            {
                // 则释放节点引用, node 节点的节点计数的内部计数 intCnt 减 1,
                nodePtr->releaseRefNode();

                // 返回空值
                return std::unique_ptr<T>();
            }

            const cntNodePtr<T> next = nodePtr->next.load();

            // 如果原节点计数指针等于 head ,
            // 曾更新 head 为下一个节点计数指针 next
            if (head.compare_exchange_strong(oldHead, next))
            {
                T *const res = nodePtr->dataPtr.exchange(nullptr);

                // 释放节点计数指针的节点的外部计数的计数 exCnters,
                // 增加节点计数指针的节点的内部计数 intCnt, 增加 exCnt - 2
                freeExternalCounter(oldHead);

                // 返回原节点计数指针持有的值
                return std::unique_ptr<T>(res);
            }

            // 则释放节点引用, node 节点的节点计数的内部计数 intCnt 减 1,
            nodePtr->releaseRefNode();
        }
    }

  private:
    // 从无锁队列中获取一个节点计数指针的引用, 并增加外部计数 exCnt
    static void increaseExternalCount(std::atomic<cntNodePtr<T>> &counter,
                                      cntNodePtr<T> &oldCounter)
    {
        // 新建节点计数指针
        cntNodePtr<T> newCounter;
        // 循环
        do
        {
            // 新节点计数指针等于原节点计数指针
            newCounter = oldCounter;
            // 增加新节点计数指针的外部计数
            ++newCounter.exCnt;
            // 当无锁队列中的节点计数指针等于原节点计数指针,
            // 用新节点计数指针更新它 否则更新原节点计数指针
        } while (!counter.compare_exchange_strong(oldCounter, newCounter,
                                                  std::memory_order_acquire,
                                                  std::memory_order_relaxed));
        // 更新原节点计数指针的外部计数
        oldCounter.exCnt = newCounter.exCnt;
    }

    // 释放节点计数指针的节点的外部计数的计数 exCnters -1,
    // 增加节点计数指针的节点的内部计数 intCnt, 增加 exCnt -2
    static void freeExternalCounter(cntNodePtr<T> &oldCntNodePtr)
    {
        // 新建节点指针, 指向原计数节点指针 oldCntNodePtr 的节点指针 nodePtr
        node<T> *const newNodePtr = oldCntNodePtr.nodePtr;

        // 计数增加值, 为原计数节点指针外部计数减二
        const int countIncrease = oldCntNodePtr.exCnt - 2;

        // 获取原节点计数
        nodeCounter oldCounter = newNodePtr->nodeCnter.load();

        // 新建节点计数
        nodeCounter newCounter;

        // 循环, 将原计数节点指针的外部计数转移到节点计数的内部计数
        do
        {
            // 将原节点计数赋值给新节点计数
            newCounter = oldCounter;

            // 新节点计数的外部计数的计数减一
            --newCounter.exCnters;

            // 新节点计数的内部计数增加 countIncrease
            newCounter.intCnt += countIncrease;

            // 当 newNodePtr 的节点计数等于原节点计数, 更新 newNodePtr 节点计数
            // 否则更新 oldCounter, 继续循环
        } while (!newNodePtr->nodeCnter.compare_exchange_strong(
            oldCounter, newCounter, std::memory_order_acquire,
            std::memory_order_relaxed));

        // 如果节点计数的内部计数和外部计数的计数都为 0,
        // 则删除 newNodePtr 指向的节点
        if ((newCounter.intCnt == 0U) && (newCounter.exCnters == 0U))
        {
            delete newNodePtr;
        }
    }

    // 设置新 tail 节点
    void setNewTail(cntNodePtr<T> &oldTail, const cntNodePtr<T> &newTail)
    {
        // 新建节点指针
        node<T> *const currentTailNodePtr = oldTail.nodePtr;

        // 若 tail 等于 oldTail, 则更新 tail, 跳过循环
        // 否则更新 oldTail, 跳过循环
        // 若 weak 伪失败, 则继续循环
        while (!tail.compare_exchange_weak(oldTail, newTail) &&
               oldTail.nodePtr == currentTailNodePtr)
        {}
        // 若 tail 更新为 newTail 则释放 oldTail 的外部引用计数
        if (oldTail.nodePtr == currentTailNodePtr)
        {
            freeExternalCounter(oldTail);
        }
        // 否则释放 currentTailNodePtr 节点的内部引用
        else
        {
            currentTailNodePtr->releaseRefNode();
        }
    }

    // 头节点计数指针
    std::atomic<cntNodePtr<T>> head;
    // 尾节点计数指针
    std::atomic<cntNodePtr<T>> tail;
};

} // namespace noLock

auto main() -> int
{
    noLock::lockFreeQueue<int> lfs;

    // std::cout << lfs.isNoLock() << std::endl;

    std::thread t0([&lfs] {
        for (int i = 0; i != 100000; ++i)
        {
            lfs.push(i);
        }
    });

    std::thread t1([&lfs] {
        for (int i = 0; i != 100; ++i)
        {
            if (lfs.pop())
            {
                //     std::cout << 1 << "\n";
            }
        }
    });

    //    std::thread t2([&lfs] {
    //        for (int i = 0; i != 500; ++i)
    //        {
    //            std::cout << *lfs.pop() << "\n";
    //        }
    //    });
    //
    //    std::thread t3([&lfs] {
    //        for (int i = 0; i != 500; ++i)
    //        {
    //            std::cout << *lfs.pop() << "\n";
    //        }
    //    });
    //
    //    std::thread t4([&lfs] {
    //        for (int i = 0; i != 500; ++i)
    //        {
    //            std::cout << *lfs.pop() << "\n";
    //        }
    //    });
    //
    //    std::thread t5([&lfs] {
    //        for (int i = 0; i != 500; ++i)
    //        {
    //            std::cout << *lfs.pop() << "\n";
    //        }
    //    });
    //
    //    std::thread t6([&lfs] {
    //        for (int i = 0; i != 500; ++i)
    //        {
    //            std::cout << *lfs.pop() << "\n";
    //        }
    //    });

    t0.join();
    t1.join();
    //    t2.join();
    //    t3.join();
    //    t4.join();
    //    t5.join();
    //    t6.join();
    lfs.push(1);
    std::cout << *lfs.pop() << std::endl;

    return 0;
}

当然, 这种数据结构有个天然的弱势, 就是不停的内存分配, push new 一个 pop delete 一个, 为了内存回收, 需要附加很多的复杂结构, 这也是无锁并发结构的结症所在, 复杂, 逻辑难以理清, 需要比有锁结构花费更多的时间去设计, 并且需要更多的测试, 以保证使用的正确和高效.


总结

本文中无锁队列的实现比无锁栈的实现要更难理解一些, 参考书中有些代码有 bug, 比如节点计数类, 如果是无符号类, 则内部计数会出现问题, 不能得负值, 可能会造成内存泄漏.

    struct node_counter
    {
        unsigned internal_count:30;  // 错误, 无法为负值, 会导致溢出或内存泄漏
        unsigned external_counters:2;
    };

这个结构实现如果能啃下来, 至少在无锁编程方面, 应该是可以说说话了.

参考文献:C++并发编程实战(第2版)[英] 安东尼•威廉姆斯(Anthony Williams)


老林的C语言新课, 想快速入门点此 <C 语言编程核心突破>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不停感叹的老林_<C 语言编程核心突破>

不打赏的人, 看完也学不会.

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值