在一个系统中有一个写线程和若干个读线程,读写线程通过一个指针共用了一个数据结构,写线程改写这个结构,读线程读取该结构。在写线程改写这个数据结构的过程中,加锁情况下读线程由于等待锁耗时会增加。
可以利用RCU (Read Copy Update What is rcu)的思想来去除这个锁。本文提到的主要实现代码:gist
RCU
RCU可以说是一种替代读写锁的方法。其基于一个事实:当写线程在改变一个指针时,读线程获取这个指针,要么获取到老的值,要么获取到新的值。RCU的基本思想其实很简单,参考What is RCU中Toy implementation可以很容易理解。一种简单的RCU流程可以描述为:
写线程:
old_ptr = _ptr
tmp_ptr = copy(_ptr) // copy
change(tmp_ptr) // change
_ptr = tmp_ptr // update
synchroize(tmp_ptr)
写线程要更新_ptr
指向的内容时,先复制一份新的,基于新的进行改变,更新_ptr
指针,最后同步释放老的内存。
读线程:
tmp_ptr = _ptr
use(tmp_ptr)
dereference(tmp_ptr)
读线程直接使用_ptr
,使用完后需要告诉写线程自己不再使用_ptr
。读线程获取_ptr
时,可能会获取到老的也可能获取到新的,无论哪种RCU都需要保证这块内存是有效的。重点在synchroize
和dereference
。synchroize
会等待所有使用老的_ptr
的线程dereference
,对于新的_ptr
使用者其不需要等待。这个问题说白了就是写线程如何知道old_ptr
没有任何读线程在使用,可以安全地释放。
这个问题实际上在wait-free
的各种实现中有好些解法,how-when-