一个无锁编程的问题

2014-09-15 15:20:45 +08:00
 alexapollo
在写程序的时候,见到一个蛮巧妙的思路(一写者,多读者):
维持两块内存,A1、A2,代表同一个对象
假如一开始读A1,那么就写A2,写完以后置一个标志位,让读写者倒换,也即之后的读者就读A2了,而写者写A1。

有人见过类似的思路吗?它是否有一个名字?
我想了很久,只觉得它很类似read-copy-update,但RCU在用户态用起来好像有点麻烦。不知道有没有更方便的实现?
6120 次点击
所在节点    程序员
26 条回复
xylophone21
2014-09-16 09:26:24 +08:00
WKPlus
2014-09-16 12:56:08 +08:00
见过很多这类应用,双buffer切换呀,应用场景是这样的:
1.多线程服务端程序,使用本地文件作为词表,有一个线程专门来检查文件是否更新,检测到更新之后,需要把新文件load到内存中的
2.每次前端有请求过来的时候,使用内存中的词表做查询,对内存中的词表只要求完整性(即不能使用更新了一半的词表),但是及时性要求不是那么高

所以做法就如题主所说,开了两个buffer,在读buffer 0的时候就写buffer 1,写完之后更新读buffer的标识(原子操作)即可。

这里其实有一个race condition:
如果有线程在读buffer 0,然后buffer 1写完成了,标识读buffer为1,又检测到更新开始写buffer 0,如果原来读buffer 0的线程还没有结束,就可能出现数据不完整的情况。

但在实际上,是不会发生的,因为每次更新完文件之后,至少要等待5min才会去检测文件是否更新,而我们服务最长的请求处理时间不会有5min这么长,所以上面的race condition就不会发生,因此也就可以无锁了。
semicircle21
2014-09-16 13:43:02 +08:00
@alexapollo 是的, 应用这个思路要有一些前提的, 我又想了一下, 对标志位加锁也不能保证不读到脏数据. 关键是改标志位的时候, 要有前提条件或机制保证此时没有正在读的线程.
我感觉, 一般无锁/同步问题都是具体问题具体分析的, 有一些常用的 Pattern, 但能直接用上的很少. 因为涉及到的细节很多, 比如时间短的操作, 在kernel 里可以关中断自旋锁来保证同步, 但在多处理器场景又不行了, 然后还要保证访问的内存是已经在 cache 里的, 否则怎么也不会是"时间短的"...
所以, 针对一般情况, 如果仅仅是追求数据吞吐量, Share information by queue, 比 memory 靠谱多了.
glogo
2014-09-16 20:43:47 +08:00
那标志位的读写不需要同步吗
nelson
2014-09-17 23:52:30 +08:00
@alexapollo 其实就是i = (i + 1) % 2
不过这种方法确实会像LSSS+说的,读A的过程中连续写了两次数据(B、A),会导致读到脏数据
pslydhh
2021-01-29 13:02:30 +08:00
用户态 RCU 哪里有资料介绍吗

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/133566

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX