我正在研究非垃圾收集环境(例如C或C++)中用于无锁数据结构的各种类型的内存回收策略。
在我的实验中,我成功地实现了其中一些策略-具体来说,包括基于静态状态的回收(QSBR)和基于纪元的回收(EBR)。
我的问题与这两种策略之间的主要区别之一有关。
首先,我知道 QSBR 和 EBR 都是如何工作的,并且已经成功实现了这两种策略。 QSBR和EBR实际上非常相似。两者都是延迟回收策略-意思是,它们在分配内存时通过简单地延迟实际的重新分配,直到可以证明释放内存是安全的,从而避免了竞争状况。对于QSBR和EBR,这都是通过使用全局“时期计数器”,然后为每个参与线程使用各种线程局部时期计数器来实现的。
QSBR和EBR之间的主要区别在于,使用QSBR时,您基本上可以指出线程何时没有对任何共享数据的引用。使用EBR,您可以指示线程何时确实具有对共享数据的引用。因此,在实践中,使用EBR的代码最终看起来更像是传统的互斥锁锁定/解锁关键部分,例如:
enter_critical_section();
/* do some cool lock-free stuff */
exit_critical_section();
...而使用QSBR,它更像是:
/* do some cool lock-free stuff */
quiescent_state(); // this thread is done using shared data
因此它们非常相似。但是,我不真正了解的一件事是,所有文献都表明,在实践中,QSBR有一个主要缺点:它需要应用程序级别的支持,这意味着它实际上不适合在通用库中使用。
无数的期刊文章或图书馆文档(例如http://www.cs.toronto.edu/~tomhart/papers/tomhart_thesis.pdf)中都提到了这一点,它说:
User-space RCU project的文档(使用QSBR的变体)也表示类似的内容:
我很难理解为什么这是一个问题。我在这里收集到的是,使用QSBR,应用程序需要指出何时进入静态状态。但是我不明白为什么在图书馆这个级别上很难做到这一点?
提供诸如堆栈和队列之类的数据结构的无锁库难道不能简单地表明每个操作完成后它都进入了静态状态吗?为什么所有关于QSBR的警告都表明,与应用程序代码相比,它在某种程度上不容易在库代码中使用?
最佳答案
在QSBR中,可以在调用线程不保留对共享对象的引用的任意位置调用quiescent_state()
。另一方面,在EBR中,线程必须才能访问由enter_critical_section()
和exit_critical_section
注释的关键部分内的共享对象。
这种差异意味着: