老林的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 语言编程核心突破>