一种无锁的并发读写机制

本文探讨了在多线程环境下如何实现无锁的并发读写机制,特别是针对ksearch系统中searcher和updater进程的数据共享。通过使用lease机制,确保在更新时不加锁,同时保持搜索的正确性。文中还讨论了指针赋值的原子性,以及在X86架构下哪些操作是原子的,哪些需要同步。此外,还分析了位域(Bit field)的线程安全性问题,并给出了使用原子操作的建议。
摘要由CSDN通过智能技术生成

ksearch系统中有两个角色会共享数据:searcher和updater,searcher是多线程读,对外提供检索服务;updater是单线程,对外提供更新服务。searcher和updater是一台机器上的两个独立进程,通过共享内存实现数据共享。searcher可以看到两份数据,一份是全量数据,以类似数组的形式组织数据;一份是增量数据,由updater进程提供,以单链表的形式组织数据。为了防止更新加锁,影响读取的性能,我们在searcher和updater之间使用了一种无锁的机制。

我们使用了lease机制来实现无锁。具体做法是,在增量doc的数据结构上添加了2个字段,一个时间字段,用来记录该条doc从链表中删除的时间;一个lease指针,维护删除的doc链表。当有一个更新操作过来,从单链表中找到原有记录(为了加速查找,单链表是经过排序的,而且加了跳表结构),把它添加到lease链表尾部,重置时间字段为当前,但是不改变原有的增量单链表结构;然后找一个空闲doc,做更新操作,然后加入到增量单链表结构中(原记录所在位置)。这样即使search在读过程中,doc发生了更新,search要么拿到更新前的doc,要么拿到更新后的doc,由于更新前后增量链表都是完成的,所以不会影响search的读。同时由于search的单个query处理时间一般在1s之内,我们为lease链表设置了一个过期时间,比如10s,保证所有还在使用lease链表中doc的search线程都已经结束。这样更新取空闲doc的时候,可以先检查lease链表的头部doc,是否已过lease时间,过的话就可以拿出来安全使用。

在上述的方案中,我们认为,单链表的操作过程(指针赋值)是原子的。即64位机器,指针是8字节,不会出现低32位完成了赋值,高32位还没赋值就被CS走了的情况;如果在中间被CS走了,就会出现指针错乱,是不可承受的。我们做了简单的实验,查看汇编代码,可以看到指针赋值有二条指令,先把源指针mov到寄存器,再把寄存器mov到目标指针。CPU的一条指令是原子的,所以指针赋值不会错乱。从实验来看,不管目标指针,有没有64位对齐,都是原子的;确切的,不是说指针赋值是原子的,而且目标指针要么是新值要么是老值,不会出现错乱;为安全起见,在实际应用中,我们要求链表指针按照8字节对齐。

发现一个问题: int64_t value = 0x1111111122222222,这个赋值是高32位和低32位分成两个指令做的;int64_t value = other 这个是原子的。


这篇文章可以作为很好的参考:http://blog.csdn.net/gantleman/article/details/6254266

多线程程序中操作的原子性

0. 背景

原子操作就是不可再分的操作。在多线程程序中原子操作是一个非常重要的概念,它常常用来实现一些同步机制,同时也是一些常见的多线程Bug的源头。本文主要讨论了三个问题:1. 多线程程序中对变量的读写操作是否是原子的?2. 多线程程序中对Bit field(位域)的读写操作是否是线程安全的?3. 程序员该如何使用原子操作?

1. 多线程环境下对变量的读写操作是否是原子的?

我们先从一道很热门的百度笔试题讲起。很多人讲不清楚其背后的原理,下面我们就来对它进行一下剖析(其实这个题目有点歧义,后面我们会讲到):

以下多线程对int型变量x的操作,哪几个需要进行同步:( )
A. x=y; B. x++; C. ++x; D. x=1;

要彻底理解这个问题,我们首先需要从硬件讲起。以常见的X86 CPU来说,根据Intel的参考手册

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值