读者写者问题
1. 读者写者问题
读者优先: 只要有一个Reader处于活动状态, 那么后面来的Reader都会被接纳. 若Reader源源不断, 那么Writer就会一直处于阻塞状态, 即写者被饿死.
写者优先: 一旦Writer就绪, 就会先执行Writer, 写者优先级高于读者, 若Writer源源不断, 那么Reader就会一直处于阻塞状态, 即读者被饿死.
2. 信号量 - 读者优先
数据结构
1 data // 共享数据 2 CountMutex = 1 // 保证Rcount互斥 3 WriterMutex = 1 // 读者写者互斥 4 Rcount = 0 // 读者数量
写者
1 sem_wait(WriterMutex); 2 3 write data; 4 5 sem_post(WriterMutex);
读者
1 sem_wait(Rcount); 2 // 第一个读者进入, 获取共享区操作权限 3 if (Rcount == 0) sem_wait(WriterMutex); 4 ++Rcount; 5 sem_post(Rcount); 6 7 read data; 8 9 sem_wait(Rcount); 10 --Rcount; 11 // 最后一个读者退出, 释放共享区操作权限 12 if (Rcount == 0) sem_post(WriterMutex); 13 sem_post(Rcount);
3. 管程 - 写者优先
数据结构
AR = 0 // 正在读的读者 AW = 0 // 正在写的写者 WR = 0 // 正在等待的读者 WW = 0 // 正在等待的写者 Condition okToRead // 条件变量: 可读 Condition okToWrite // 条件变量: 可写 Lock lock // 线程互斥锁
读者
1 public Database::Read() { 2 // wait until no writer 3 StartRead(); 4 5 read database; 6 7 // check out - wake up waiting writers 8 DoneRead(); 9 } 10 11 private Database::StartRead() { 12 lock.Acquire(); 13 // 若存在正在写或正在等待的写者, 就将读者加入等待队列中 14 while ((AW + WW) > 0) { 15 WR++; 16 okToRead.wait(&lock); // 睡眠, 被唤醒后从这里开始执行 17 WR--; 18 } 19 // 允许同时存在多个读者 20 AR++; 21 lock.Release(); 22 } 23 24 private Database::DoneRead() { 25 lock.Acquire(); 26 AR--; 27 // 若最后一个正在读的读者退出且有写者在排队, 那么就唤醒一个写者 28 if (AR == 0 && WW > 0) { 29 okToWrite.signal(); // 唤醒一个写者 30 } 31 lock.Release(); 32 }
写者
1 public Database::Write() { 2 // wait until no readers or writers 3 StartWrite(); 4 5 write database; 6 7 // check out - wake up waiting writers or readers 8 DoneWrite(); 9 } 10 11 private Database::StartWrite() { 12 lock.Acquire(); 13 // 若存在正在操作数据库的对象, 就等待 14 // 由于 AW 也包含, 所以一次只允许一个读者操作数据库 15 while ((AW + AR) > 0) { 16 WW++; 17 okToWrite.wait(&lock); 18 WW-- 19 } 20 AW++; 21 lock.Release(); 22 } 23 24 private Database::DoneWrite() { 25 lock.Acquire(); 26 AW--; 27 // 若存在正在等待的写者, 则唤醒随机一个 28 if (WW > 0) { // 条件1 29 okToWrite.signal(); 30 } 31 // 若条件1不满足且存在正在等待的读者, 则唤醒全部正在等待的读者 32 else if (WR > 0) { 33 okToWrite.broadcast(); 34 } 35 lock.Release(); 36 }