读者写者问题的优先级与读写锁的实际解决方案
引言
题目其实可以说清楚这个问题,但是确实是有点长。在看到读者写者问题的各种优先级解决方案(读者优先,写者优先,读者写者公平)以后联想到以前思考过的一个问题,即写锁饥饿问题(传送门),那个问题的本质就是读者写者的优先级问题,且当时找到的对应解法是比较优秀的解法,那篇文章描述的两种解法其实都可以算是写者读者公平的方法(cow算是并行)。那么有所偏重的优先级解法该如何做呢?其实也很简单,这篇文章会使用伪代码来一一做解释,注意使用会信号量作为同步工具。这篇文章算是对这个问题的一个小总结,因为原来的那篇文章可以看做这篇文章的一个拓展,而原来文章的内容现在看来着实是十分有趣的,遂记录这篇文章,以引出那篇文章,希望借此让更多的人对这个问题的看法更深一些。
问题描述
首先简单描述一下读者写者问题:
读者-写者问题是指保证任何写者进程必须与其他进程互斥的访问共享的数据对象的同步问题。
- 存在多个进程共享一个数据对象。
- 仅要求读的进程称为读者进程。
- 要求写的进程称为写者进程。
- 允许多个读者进程同时执行读操作。
- 写者进程具有排他性(写时不允许读者线程读,写者线程写)。
从对问题的描述我们可以看出其实读写锁实际上是读者-写者问题的一个实体,且运用是比较广泛的。
读者优先
读者优先意味着当目前存在读进程的时候写进程会被无限延后执行,当不存在读进程的时候写进程才可以执行,可以简单的理解为“读进程插队”。
其中的wnutex,rmutex是信号量,使用PV操作(wait,signal),当然就这里而言看做互斥锁也是可以的。
我们简单的分析以下这个过程,当有一个读者的时候就把readercount
加一,也就是说readercount记录着现在的读者数,且在存在读者的时候wait(wmutex)
,这样当存在读者的时候写者就无法执行,而在读者存在的时候所有后来的读者是可以“插队”的,这样就做到了读者优先。
读者-写者公平
读者写者公平显然是一个比较常见的模型,那么我们如何可以做到读者写者公平呢?我在写锁饥饿问题中描述了两种性能优秀的算法,这里就不再细谈了。我们使用最简单的加锁来实现一个读者-写者公平算法:
我们引入一个互斥锁S,在每次读进程执行的时候要请求S,只有得到才能继续,对应的,如果写进程在写入时也必须拿到S的话就会使得写进程不执行的话,写进程之后发生的读请求就会阻塞了,总而实现公平。但是缺陷显然也非常明显,就是所有操作都需要请求S,这样当操作线程多了以后S就会成为瓶颈。
写者优先
当写入优先级较高的时候我们希望写者优先,实际上这个模型使用我在写锁饥饿问题中提到的copy-on-write
(不是fork优化的那个)的话效率更高,但当copy成本过高的时候就需要其他的方法了(这样看来两篇文章是互补的),这里使用锁来完成。
任何一个读者被调用的时候都需要与写者竞争S。
其实逻辑和读者优先的时候一样,及时使写进程成为一个团体,只有在全部的写操作完成的时候,即writercount==0
的时候才会释放S,这个时候读者才可以继续读,这样看来就是写者可以“插队”,当然第一个写者与读者的竞争与读者-写者公平的过程是一样的。
参考:
- 课程《北京交通大学 操作系统》
- 博文《读写锁中写锁的饥饿问题》