Hazard Pointers:无锁数据结构删除节点后,谁来保证它还没被读到?
TL;DR
Hazard Pointers 让线程在访问节点前公开保护标记,删除者延迟回收并扫描这些标记,从而避免悬空指针与部分 ABA 风险。
Hazard Pointers 让线程在访问节点前公开保护标记,删除者延迟回收并扫描这些标记,从而避免悬空指针与部分 ABA 风险。

无锁数据结构最容易被忽略的一幕,发生在“删除”之后。一个线程把链表节点从结构中摘下来,并不意味着另一个线程不会马上解引用它。如果节点被立刻释放,后来的读取就可能变成悬空指针。Hazard Pointers(危险指针)是一套让无锁回收变得可验证的办法。
它的做法很像图书馆的“正在阅读”登记。线程准备访问一个可能被删除的节点时,先把节点地址写入自己的危险指针槽位,再重新确认节点仍然在链表中。如果节点在这两步之间被别人摘走,线程发现校验失败,就放弃本轮访问并重试;如果节点仍然存在,它才放心读取。删除者不会立即释放摘下来的节点,而是把它放进退休列表,之后扫描所有线程公开的危险指针。
只有当一个退休节点没有出现在任何危险指针里,回收者才可以真正释放它。这个过程还要处理一个叫 ABA 的问题:地址 A 曾经被删除,内存又被重新分配给另一个对象,某个线程只比较地址时会误以为“什么都没变”。危险指针通过要求读者先公开保护、删除者延迟回收,降低了地址被过早复用造成误判的风险。
它的优势是边界清晰,适合栈、队列、哈希表等需要无锁读取和删除的结构,而且不要求所有线程同时停下来。代价是每个参与者都要维护保护槽位,删除者需要扫描这些槽位,退休对象也可能在很长时间内占用内存。代码还必须严格遵守“发布保护—重新验证—访问对象”的顺序,少一步都可能留下竞态。
所以,危险指针不是一种更快的垃圾回收器,而是一种显式的生命周期协议:我即将访问这个节点,请暂时不要回收它;等我清除保护后,系统才有资格判断它是否安全。理解这个协议,才能看懂很多看似没有锁、实际依靠精细内存管理维持安全的并发库。
资料来源:Hazard Pointers:Safe Memory Reclamation for Lock-Free Objects



