seqlock和rwlock

最近看《linux内核设计与实现》时看到一种叫seqlock的锁,其作用与rwlock有相仿的功能。

在读写频率不对等的情况下,使用普通的互斥锁显然并不是一个好主意。

由于读取内存中的数据并不会产生副作用(嵌入式除外),因此在数据不改变的情况下,多个读操作可以并发执行。

在读频率远大于写频率时,如果仅仅将写操作与读操作互斥,读与读之间并发执行,显然可以大幅提高程序的性能。

所以在这个时候,读写锁应运而生。使用读写锁时,多个读操作可以并发进行,但是只要有读操作在进行,写操作就必须等待。

其实现原理并不复杂。

获取读锁前检查是否写锁已经被获取,如果获取则等待。如果写锁没有被获取,则对读者数加一。释放读锁时对读者数减一即可。

获取写锁时,直到读者数为0才可以将写锁置为获取状态。

以前也写过一个类似的rwlock, 并在此基础上增加了写与写互斥和写操作防饿死机制(为了支持多线程并发写)。


从读写锁的实现上来看,读写锁对读比较优待,只要有读操作就写操作就必须等待。

如果某块内存仅有一个线程很频繁的写,其他线程只是偶尔读一下,并且要对写非常优待,即有线程的写操作不能被打断。

这时候seqlock就派上用场了。

seqlock的实现更为简单。

每一个seqlock维护一个index索引值,每次向seqlock获取写锁时总能成功,并把seqlock的index自增。

每次读操作前后都获取一下seqlock的index索引值,如果前后获取的index值并不相同,则重新读取数据。

从上面机制可以看出,与rwlock相似都是对某一频繁操作进行优待,但与rwlock不同的是,seqlock是对写操作优待。

POSIX线程中的条件变量与互斥量

在看到UNP第26章时发现对于linux下的线程模型还很不是非常了解. 翻到APUE的第11章, 发现POSIX线程中通用的两个线程交互与同步手段有互斥量和条件变量两种.

对于这两种同步手段我想了很久也没想到每种到底适用于哪些场景, 甚至于我觉得几乎mutex就够完成绝大部分线程同步功能了. 以前在写代码时都是自然而然的用出来了, 也从来没有认真总结过到底什么情况下使用Mutex, 什么下使用条件变量(类似Windows下的事件).

google到这篇文章终于豁然开朗. 其实问题很简单, 也许换个描述方式就更容易说明问题, Mutex是用来在线程内来保证操作的原子性, 可以称为同步手段. 而条件变量是用来线程间通信使用的. 如Mutex在某一线程Get之后, 从此这个Mutex就属于这个线程, 只有Get到这个Mutex的线程才能将其释放. 而条件变量则不然, 线程A设置过条件变量后, 线程B即可访问, 反之线程B设置条件变量之后线程A也可以访问. 因此多线程编码中当需要保证于某一操作的原子性时则使用Mutex, 当线程间需要相互通信时则需要使用条件变量.


在下班回来的路上, 大概回忆了一下代码中多线程的地方, 发现其实并非没有用到条件变量(Windows下的事件)的地方, 只是因为我都是顺手就写成while (flag == flase) sleep(x)这种查询的方式, 如果将查询换成条件变量之后, 此线程被唤醒的次数将大大降低对CPU时间的浪费将会更少.


1月14日补充:
条件变量只有在pthread_cond_wait进入sleep之后, pthread_cond_signal函数执行才能将pthread_cond_wait函数唤醒, 如果一个线程先执行pthread_cond_signal函数, 随后另一个线程执行到pthread_cond_wait时依然会被挂起. 这点与Windows下的Event并不相同, Windows下的Event不论是先于WaitForSingleObject去设置Event还是后于WaitForSingleObject, 只要Event被设置WaitForSingleObject都能正常返回.