Skip to content

Files

Latest commit

8284d54 · Jul 11, 2019

History

History
224 lines (172 loc) · 6.23 KB

37.md

File metadata and controls

224 lines (172 loc) · 6.23 KB

同步,第 7 部分:读者编写器问题

原文:https://github.com/angrave/SystemProgramming/wiki/Synchronization%2C-Part-7%3A-The-Reader-Writer-Problem

什么是读者作家问题?

想象一下,你有一个许多线程使用的键值映射数据结构。如果没有写入数据结构,多个线程应该能够同时查找(读取)值。作者不是那么合群 - 为了避免数据损坏,一次只有一个线程可以修改(write)数据结构(当时没有读者可能正在阅读)。

这是 _ 读写器问题 _ 的一个例子。也就是说,我们如何有效地同步多个读者和作者,以便多个读者可以一起阅读,但作家获得独家访问?

下面显示了不正确的尝试(“lock”是pthread_mutex_lock的简写):

尝试#1

|

read() {
  lock(&m)
  // do read stuff
  unlock(&m)
}

|

write() {
  lock(&m)
  // do write stuff
  unlock(&m)
}

|

至少我们的第一次尝试不会遭受数据损坏(读者必须在作家写作时等待,反之亦然)!但读者也必须等待其他读者。那么让我们尝试另一种实现..

尝试#2:

|

read() {
  while(writing) {/*spin*/}
  reading = 1
  // do read stuff
  reading = 0
}

|

write() {
  while(reading || writing) {/*spin*/}
  writing = 1
  // do write stuff
  writing = 0
}

|

我们的第二次尝试遭遇竞争条件 - 想象两个线程是否同时调用readwrite(或两者都称为写入)。两个线程都可以继续!其次,我们可以有多个读者和多个作者,所以让我们跟踪读者或作者的总数。这让我们尝试#3,

尝试#3

请记住pthread_cond_wait执行 _ 三个 _ 动作。首先,它以原子方式解锁互斥锁然后休眠(直到它被pthread_cond_signalpthread_cond_broadcast唤醒)。第三,唤醒线程必须在返回之前重新获取互斥锁。因此,实际上只有一个线程可以在 lock 和 unlock()方法定义的临界区内运行。

下面的实现#3 确保如果有任何作者写作,读者将进入 cond_wait。

read() {
    lock(&m)
    while (writing)
        cond_wait(&cv, &m)
    reading++;

/* Read here! */

    reading--
    cond_signal(&cv)
    unlock(&m)
}

然而,只有一个读者一次可以阅读,因为候选人#3 没有解锁互斥锁。更好的版本在阅读之前解锁:

read() {
    lock(&m);
    while (writing)
        cond_wait(&cv, &m)
    reading++;
    unlock(&m)
/* Read here! */
    lock(&m)
    reading--
    cond_signal(&cv)
    unlock(&m)
}

这是否意味着作者和阅读可以同时读写?没有!首先,请记住 cond_wait 要求线程在返回之前重新获取互斥锁。因此,一次只有一个线程可以在临界区内执行代码(用**标记)!

read() {
    lock(&m);
**  while (writing)
**      cond_wait(&cv, &m)
**  reading++;
    unlock(&m)
/* Read here! */
    lock(&m)
**  reading--
**  cond_signal(&cv)
    unlock(&m)
}

作家必须等待每个人。锁定可确保相互排斥。

write() {
    lock(&m);
**  while (reading || writing)
**      cond_wait(&cv, &m);
**  writing++;
**
** /* Write here! */
**  writing--;
**  cond_signal(&cv);
    unlock(&m);
}

上面的候选人#3 也使用pthread_cond_signal;这只会唤醒一个线程。例如,如果许多读者等待作者完成,那么只有一个睡觉的读者会从他们的睡眠中醒来。读写器应该使用cond_broadcast,以便唤醒所有线程并检查它们的 while 循环条件。

饥饿的作家

上面的候选人#3 患有饥饿。如果读者经常到达,那么作家永远无法继续(“阅读”计数永远不会减少到零)。这被称为 _ 饥饿 _,并将在重负荷下被发现。我们的解决方法是为作者实现有限等待。如果作家到了,他们仍然需要等待现有的读者,但是未来的读者必须被置于“握笔”中并等待作者完成。 “握笔”可以使用变量和条件变量来实现(这样我们就可以在编写完成后唤醒线程)。

我们的计划是,当作家到来时,在等待当前读者完成之前,记录我们的写作意图(通过递增计数器“作者”)。草绘如下 -

write() {
    lock()
    writer++

    while (reading || writing)
    cond_wait
    unlock()
  ...
}

当作家非零时,传入的读者将不被允许继续。注意'作家'表示作家已到达,而'阅读'和'写'计数器表示有 _ 有效 _ 读者或作者。

read() {
    lock()
    // readers that arrive *after* the writer arrived will have to wait here!
    while(writer)
    cond_wait(&cv,&m)

    // readers that arrive while there is an active writer
    // will also wait.
    while (writing) 
        cond_wait(&cv,&m)
    reading++
    unlock
  ...
}

尝试#4

以下是我们对 Reader-Writer 问题的第一个解决方案。请注意,如果您继续阅读“Reader Writer 问题”,那么您会发现我们通过给予编写者优先访问锁来解决“第二个 Reader Writer 问题”。该解决方案不是最佳的。然而,它满足了我们原来的问题(N 个活跃的读者,单个活跃的作家,如果有一个持续的读者流,避免作家的饥饿)。

你能找出任何改进吗?例如,你如何改进代码,以便我们只唤醒读者或一个作家?

int writers; // Number writer threads that want to enter the critical section (some or all of these may be blocked)
int writing; // Number of threads that are actually writing inside the C.S. (can only be zero or one)
int reading; // Number of threads that are actually reading inside the C.S.
// if writing !=0 then reading must be zero (and vice versa)

reader() {
    lock(&m)
    while (writers)
        cond_wait(&turn, &m)
    // No need to wait while(writing here) because we can only exit the above loop
    // when writing is zero
    reading++
    unlock(&m)

  // perform reading here

    lock(&m)
    reading--
    cond_broadcast(&turn)
    unlock(&m)
}

writer() {
    lock(&m)  
    writers++  
    while (reading || writing)   
        cond_wait(&turn, &m)  
    writing++  
    unlock(&m)  
    // perform writing here 
    lock(&m)  
    writing--  
    writers--  
    cond_broadcast(&turn)  
    unlock(&m)  
}