无锁栈的一种实现方式

本文中无锁栈的实现,基于对《c++并发编程实践》的学习,对其他无锁数据结构的实现也有借鉴意义。

先看无锁栈的最初版本:

template <typename T>
class lockfreestack
{
	struct node
	{
		std::unique_ptr<T> data;
		node *next;
	};
	std::atomic<node*> head{nullptr};
public:
	void push(T data)
	{
		node *new_node = new node;
		new_node->data = new T(std::move(data));
		new_node->next = head.load();
		while(!head.compare_exchange_weak(new_node->next, new_node));
	}
	std::unique_ptr<T> pop()
	{
		node *old_head = head.load();
		while(old_head && !head.compare_exchange_weak(old_head, old_head->next)); // I
		return old_head ? std::move(old_head->data) : nullptr;
	}
};

还不错,除了有内存泄露。在pop中,compare_exchange_weak成功的线程是不能直接delete old_head;的,因为其他线程可能拿着同样的指针,在取old_head->next。

很自然的想法,要是现在不能释放,那就先收集起来,等能释放的时候释放。
可能有问题的地方在于I的old_head->next,可以维护一个原子变量计数器,进入I之前加1,离开I后减1,当计数器值不为0时,就把要删除的指针存起来,当计数器为0时,就可以把当前的old_head以及之前暂存的指针一起删除掉了。实现细节可参考 chest 中的 lockfreestack_origin.h文件。

上面的计数方法,粒度有点大,处于I处的线程,可能拿到的不是同样的指针,但会导致所有的待删除指针都无法释放。
这里介绍一种风险指针的方法。考虑到每个线程最多只能占用着一个old_head,可以记下每个线程占用着的指针,当想要释放一个指针时,看一下是否有其他线程在占用这个指针,如果没有的话,就可以释放掉。可参考 chest 中的 lockfreestack2.h文件。

第二种方法粒度细一些,可以及时的释放掉内存,但增加了一些计算,可能会得不偿失。

另外,处理失效指针的方法还有epoch-based reclamationinterval-based reclamation等。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值