Multithreading 使用危险指针进行无锁内存回收
是一种在无锁代码中安全回收内存而无需垃圾回收的技术 其思想是,在访问可以同时删除的对象之前,线程将其危险指针设置为指向该对象。想要删除对象的线程将首先检查是否有任何危险指针被设置为指向该对象。如果是这样,删除将被推迟,这样访问线程就不会读取已删除的数据 现在,假设我们的删除线程开始迭代危险指针列表,并在Multithreading 使用危险指针进行无锁内存回收,multithreading,algorithm,memory-management,lock-free,Multithreading,Algorithm,Memory Management,Lock Free,是一种在无锁代码中安全回收内存而无需垃圾回收的技术 其思想是,在访问可以同时删除的对象之前,线程将其危险指针设置为指向该对象。想要删除对象的线程将首先检查是否有任何危险指针被设置为指向该对象。如果是这样,删除将被推迟,这样访问线程就不会读取已删除的数据 现在,假设我们的删除线程开始迭代危险指针列表,并在i+1元素处被抢占。现在,另一个线程将危险指针设置为i,指向删除线程当前试图删除的对象。之后,删除线程继续,检查列表的其余部分,并删除对象,即使现在在位置i处有一个指向该对象的危险指针 显然,仅仅
i+1
元素处被抢占。现在,另一个线程将危险指针设置为i
,指向删除线程当前试图删除的对象。之后,删除线程继续,检查列表的其余部分,并删除对象,即使现在在位置i
处有一个指向该对象的危险指针
显然,仅仅设置hazard指针是不够的,因为删除线程可能已经检查了hazard指针,并决定线程不想访问该对象。设置危险指针后,如何确保我试图访问的对象不会从我手中删除
想要删除对象的线程将首先检查是否有任何危险指针被设置为指向该对象
问题就在这里“删除”实际上是两阶段操作:
D-1. Remove the node from the data structure.
D-2. Iterate the list of hazard pointers.
D-3. If no hazards were found, delete the node.
A-1. Obtain a pointer to the node by traversing the data structure.
A-2. Set the hazard pointer to point to the node.
A-3. Check that the node is still part of the data structure.
That is, it has not been possibly removed in the meantime.
A-4. If the node is still valid, access it.
真正的算法稍微复杂一些,因为我们需要维护一个无法回收的节点列表,并确保它们最终被删除。这里跳过了这一点,因为这与解释问题中提出的问题无关
它对访问线程意味着什么
设置危险指针不足以保证安全访问。毕竟,当我们设置危险指针时,节点可能会被删除
确保安全访问的唯一方法是,如果我们能够保证我们的危险指针从保证从对象的根可以访问节点时起一直指向该节点
由于代码应该是无锁的,因此只有一种方法可以实现这一点:我们乐观地将危险指针设置为指向节点,然后检查该节点是否已被标记为可能已删除(即,不再可以从公共根访问该节点)
因此,访问线程的操作顺序是
D-1. Remove the node from the data structure.
D-2. Iterate the list of hazard pointers.
D-3. If no hazards were found, delete the node.
A-1. Obtain a pointer to the node by traversing the data structure.
A-2. Set the hazard pointer to point to the node.
A-3. Check that the node is still part of the data structure.
That is, it has not been possibly removed in the meantime.
A-4. If the node is still valid, access it.
影响删除线程的潜在争用
可能删除节点(D-1
)后,删除线程可能会被抢占。因此,并发线程仍然可以乐观地将其危险指针设置为指向它(即使不允许它们访问它)(A-2
)
因此,删除线程可能会检测到虚假的危险,阻止它立即删除节点,即使其他线程都不会再访问该节点。这将简单地延迟删除节点,就像合法的危害一样
重要的一点是,节点最终仍将被删除
影响访问线程的潜在争用
在验证节点未被潜在删除之前,访问线程可能会被删除线程抢占(a-3
)。在这种情况下,不再允许它访问对象
请注意,如果抢占发生在A-2
之后,那么访问线程访问该节点甚至是安全的(因为有一个危险指针始终指向该节点),但由于访问线程无法区分这种情况,因此它必须错误地失败
重要的一点是,一个节点只有在未被删除的情况下才会被访问。看来,至少在检查其他区域的危险指针之前,删除线程必须设置自己的危险指针