问题描述:允许多个进程同时对数据进行读操作,但是不允许读和写以及写和写操作同时发生。
信号量:一个整型变量 count 记录在对数据进行读操作的进程数量,一个互斥量 count_mutex 用于对 count 加锁,一个互斥量 data_mutex 用于对读写的数据加锁。
简单信号量版本<version 1.0>:
typedef int semaphore;
semaphore count_mutex = 1;
semaphore data_mutex = 1;
int count = 0;
void reader() {
while(TRUE) {
down(&count_mutex);
count++;
if(count == 1) down(&data_mutex); // 第一个读者需要对数据进行加锁,防止写进程访问
up(&count_mutex);
read();
down(&count_mutex);
count--;
if(count == 0) up(&data_mutex);
up(&count_mutex);
}
}
void writer() {
while(TRUE) {
down(&data_mutex);
write();
up(&data_mutex);
}
}
上面这种可能导致一些情况。
第一种情况可能导致作家饿死。这种情况下,喜欢写文章的人,即任何一个写文章的人,一旦加入队列,等待的时间都不得超过绝对必要的时间(只有当有读者在作者之前进入队列时),因为读者阅读后,对数据加锁,作家不能写。
所以有了version 2.0:
int readcount, writecount; //(initial value = 0)初始值为0
semaphore rmutex, wmutex, readLock, resource; //(initial value = 1)初始值为1
//READER
void reader() {
<ENTRY Section>
down(&readLock); // reader is trying to enter 读者进入,
down(&rmutex); // lock to increase readcount 读者,down锁,互斥,防止多读者修改信号量
readcount++; //读者数量+1
if (readcount == 1) //如果是第一个读者,就要锁上文章,不让作家修改
down(&resource); //if you are the first reader then lock the resource
up(&rmutex); //release for other readers解锁,其他读者可以读了
up(&readLock); //Done with trying to access the resource
<CRITICAL Section>
//reading is performed //读者阅读
<EXIT Section>
down(&rmutex); //reserve exit section - avoids race condition with readers
readcount--; //indicate you're leaving
if (readcount == 0) //checks if you are last reader leaving
up(&resource); //if last, you must release the locked resource
up(&rmutex); //release exit section for other readers
}
//WRITER
void writer() {
<ENTRY Section>
down(&wmutex); //reserve entry section for writers - avoids race conditions
writecount++; //report yourself as a writer entering
if (writecount == 1) //checks if you're first writer
down(&readLock); //if you're first, then you must lock the readers out. Prevent them from trying to enter CS
up(&wmutex); //release entry section
<CRITICAL Section>
down(&resource); //reserve the resource for yourself - prevents other writers from simultaneously editing the shared resource
//writing is performed
up(&resource); //release file
<EXIT Section>
down(&wmutex); //reserve exit section
writecount--; //indicate you're leaving
if (writecount == 0) //checks if you're the last writer
up(&readLock); //if you're last writer, you must unlock the readers. Allows them to try enter CS for reading
up(&wmutex); //release exit section
}
我们可以看到,每个读者都被迫获得ReadLock。另一方面,作者不需要单独锁定。一旦第一个writer锁定ReadLock,它将仅在队列中没有writer时释放。
从这两个案例中,我们发现读者或作家都必须挨饿。下面的解决方案添加了一个约束,即不允许任何线程饿死;也就是说,获取共享数据锁的操作总是在有限的时间内终止。
Version 3.0
int readCount; // init to 0; number of readers currently accessing resource
// all semaphores initialised to 1
Semaphore resourceAccess; // controls access (read/write) to the resource
Semaphore readCountAccess; // for syncing changes to shared variable readCount
Semaphore serviceQueue; // FAIRNESS: preserves ordering of requests (signaling must be FIFO)
void writer()
{
down(&serviceQueue); // wait in line to be servicexs
// <ENTER>
down(&resourceAccess); // request exclusive access to resource
// </ENTER>
up(&serviceQueue); // let next in line be serviced
// <WRITE>
writeResource(); // writing is performed
// </WRITE>
// <EXIT>
up(&resourceAccess); // release resource access for next reader/writer
// </EXIT>
}
void reader()
{
down(&serviceQueue); // wait in line to be serviced
down(&readCountAccess); // request exclusive access to readCount
// <ENTER>
if (readCount == 0) // if there are no readers already reading:
down(&resourceAccess); // request resource access for readers (writers blocked)
readCount++; // update count of active readers
// </ENTER>
up(&serviceQueue); // let next in line be serviced
up(&readCountAccess); // release access to readCount
// <READ>
readResource(); // reading is performed
// </READ>
down(&readCountAccess); // request exclusive access to readCount
// <EXIT>
readCount--; // update count of active readers
if (readCount == 0) // if there are no readers left:
up(&resourceAccess); // release resource access for all
// </EXIT>
up(&readCountAccess); // release access to readCount
}