基于指针最高16位作为标签的C++无锁队列实现疑问
并发无锁队列指针位编码问题解答
我正在实现一个并发无锁队列,打算把标签存储在指针的16个最高有效位中,参考经典无锁队列算法。但原算法的指针结构体包含独立计数变量(structure pointer_t {ptr: pointer to node_t, count: unsigned integer}),而我只能用单个指针实现,目前代码如下:
template<class P> struct pointer_t { P* ptr; // 没有额外的uint存储标签 P* address(){ return (P*)(ptr & 0x0000FFFFFFFFFFFF); } uint count(){ return ptr & 0xFFFF000000000000; } };
针对提出的三个问题,解答如下:
1. 正确提取最高16位计数并转为uint
当前count()返回的是带高位掩码的指针位模式,并非真正的uint计数。需要先将指针转成标准整数类型做位运算,再提取有效计数:
#include <cstdint> // 引入uintptr_t uint count(){ // 先转成和指针同宽的无符号整数,右移48位把高16位移到低位,再取低16位 return static_cast<uint>((reinterpret_cast<uintptr_t>(ptr) >> 48) & 0xFFFF); }
注意:标准C++不允许直接对指针做位操作,必须转成
uintptr_t(指针宽度的无符号整数)才是合规行为。
2. address()函数的正确性修正
当前实现存在未定义行为(直接操作指针位),正确写法需先转成uintptr_t处理,再转回指针:
#include <cstdint> P* address(){ auto ptr_val = reinterpret_cast<uintptr_t>(ptr); // 保留低48位地址,清除高16位的计数部分 auto addr_val = ptr_val & 0x0000FFFFFFFFFFFF; return reinterpret_cast<P*>(addr_val); }
x86-64用户态平台的虚拟地址确实只使用低48位,这个掩码是有效的;若在其他平台使用,需根据地址宽度调整掩码值。
3. 用位操作实现带计数递增的CAS
要实现原算法中<node, next.count+1>的原子更新,需把新节点地址和递增后的计数合并为一个指针值,步骤如下:
#include <cstdint> // 假设已有当前的next指针结构体和新节点 pointer_t<P> next = ...; P* new_node = ...; // 1. 计算递增后的新计数 uint new_count = next.count() + 1; // 2. 清除新节点地址的高16位(避免和计数冲突) auto new_node_val = reinterpret_cast<uintptr_t>(new_node) & 0x0000FFFFFFFFFFFF; // 3. 合并计数与地址:计数左移48位,和地址位做或运算 auto combined_val = new_node_val | (static_cast<uintptr_t>(new_count) << 48); // 4. 转成指针类型传入CAS,完成原子更新 bool success = CAS(&tail.ptr->next, next.ptr, reinterpret_cast<P*>(combined_val));
注意:x86-64用户态地址的高16位本身就是0,无需额外处理;若平台地址范围不同,需确保地址不会占用计数位。
内容的提问来源于stack exchange,提问作者callum arul
相关产品推荐
相关产品推荐

