readers-writers problems

本文探讨了计算机科学中常见的并发问题——读者-写者问题,并详细介绍了三种解决方案:第一读者-写者问题、第二读者-写者问题和第三读者-写者问题。通过引入读锁和写锁的概念,文章阐述了如何在共享内存环境下实现读操作的并发性和写操作的互斥性,同时避免饥饿现象的发生。

In computer science, the first and second readers-writers problems are examples of a common computing problem in concurrency. The two problems deal with situations in which many threads must access the same shared memory at one time, some reading and some writing, with the natural constraint that no process may access the share for reading or writing while another process is in the act of writing to it. (In particular, it is allowed for two or more readers to access the share at the same time.) A readers-writer lock is a data structure that solves one or more of the readers-writers problems.

The first readers-writers problem[edit]

Suppose we have a shared memory area with the constraints detailed above. It is possible to protect the shared data behind a mutual exclusion mutex, in which case no two threads can access the data at the same time. However, this solution is suboptimal, because it is possible that a reader R1 might have the lock, and then another reader R2 requests access. It would be foolish for R2 to wait until R1 was done before starting its own read operation; instead, R2 should start right away. This is the motivation for the first readers-writers problem, in which the constraint is added that no reader shall be kept waiting if the share is currently opened for reading. This is also called readers-preference, with its solution below:

semaphore wrt=1, mutex=1;
readcount=0;
 
writer()
{
    wait(wrt);
    // Writing is done
    signal(wrt);
}
 
reader()
{
    wait(mutex);
    readcount++;
    if (readcount == 1)
        wait(wrt);
    signal(mutex);
    // Do the Reading
    // (Critical Section Area)
    wait(mutex);
    readcount--;
    if (readcount == 0)
        signal(wrt);
    signal(mutex);
}

The second readers-writers problem[edit]

Suppose we have a shared memory area protected by a mutex, as above. This solution is suboptimal, because it is possible that a reader R1 might have the lock, a writer W be waiting for the lock, and then a reader R2 requests access. It would be foolish for R2 to jump in immediately, ahead of W; if that happened often enough, W would starve. Instead, W should start as soon as possible. This is the motivation for the second readers-writers problem, in which the constraint is added that no writer, once added to the queue, shall be kept waiting longer than absolutely necessary. Here, P() is for wait and V() is for signal. This is also called writers-preference.

A solution to the writers-preference scenario is presented below:[1]

int readcount, writecount; (initial value = 0)
semaphore mutex_1, mutex_2, mutex_3, w, r ; (initial value = 1)
 
READER
  P(mutex_3);
    P(r);
      P(mutex_1);
        readcount := readcount + 1;
        if readcount = 1 then P(w);
      V(mutex_1);
    V(r);
  V(mutex_3);
 
  reading is performed
 
  P(mutex_1);
    readcount := readcount - 1;
    if readcount = 0 then V(w);
  V(mutex_1);
 
WRITER
  P(mutex_2);
    writecount := writecount + 1;
    if writecount = 1 then P(r);
  V(mutex_2);
 
  P(w);
    writing is performed
  V(w);
 
  P(mutex_2);
    writecount := writecount - 1;
    if writecount = 0 then V(r);
  V(mutex_2);

The third readers-writers problem[edit]

In fact, the solutions implied by both problem statements result in starvation — the first readers-writers problem may starve writers in the queue, and the second readers-writers problem may starve readers. Therefore, the third readers-writers problem is sometimes proposed, which adds the constraint that no thread shall be allowed to starve; that is, the operation of obtaining a lock on the shared data will always terminate in a bounded amount of time.

A solution with fairness for both readers and writers might be as follows:

        semaphores: no_waiting, no_accessing, counter_mutex ( initial value is 1 ) 
        shared variables: nreaders ( initial value is 0 ) 
        local variables:  prev, current
 
WRITER:
        P( no_waiting );
        P( no_accessing );
        V( no_waiting );
          ...  write ...
        V( no_accessing );
 
READER:
        P( no_waiting );
          P( counter_mutex );
            prev := nreaders;
            nreaders := nreaders + 1;
          V( counter_mutex );
          if prev = 0  then P( no_accessing );
        V( no_waiting );
        ... read ...
        P( counter_mutex );
          nreaders := nreaders - 1;
          current := nreaders;
        V( counter_mutex );
        if current = 0 then V( no_accessing );

Note that sections protected by counter_mutex could be replaced by a suitable fetch-and-add atomic instruction, saving two potential context switches in reader's code.

Note also that this solution can only satisfy the condition that "no thread shall be allowed to starve" if and only if semaphores preserve first-in first-out ordering when blocking and releasing threads. Otherwise, a blocked writer, for example, may remain blocked indefinitely with a cycle of other writers decrementing the semaphore before it can.

See also[edit]

References[edit]

[2] [3] [4]

  1. Jump up ^ Communications of the ACM :Concurrent Control with "Readers" and "Writers" P.J. Courtois,* F. H, 1971 [1]
  2. Jump up ^ Morris JM (1979) A starvation-free solution to the mutual exclusion problem. Inf Process Lett 8:76–80
  3. Jump up ^ Fair Solution to the Reader-Writer-Problem with Semaphores only. H. Ballhausen, 2003 [2]
  4. Jump up ^ Faster Fair Solution for the Reader-Writer Problem. V.Popov, O.Mazonka 2013 [3]

操作系统代码实现:Number Project Name Content Summary State Type 一、Process Scheduling Algorithm Simulation 1、 Simulate the operation of the round-robin algorithm for process scheduling. 2、 Create at least 15 processes and output their scheduling situation under the scheduling algorithm mentioned above and output it to theterminal to check the execution of the algorithm. 3、 The output should include the arrival time of the processes, the end time, and the average execution time. Essential. General 二、Readers-Writer Problem Implmentation 1、 A data set is shared among several concurrent processes: Readers – only read the data set; they do not perform any updates. Writers – can both read and write. 2、 Problem – allow multiple readers (at most 8) to read at the same time. Only one single writer can access the shared data at the same time. Essential. General 三、Program for Least Recently used Algorithm 1、 Create a page access sequence (page number range 0-18) using a random function. The sequence length is 54 and assume that the number of main memory frames allocated to the thread is 6, that is, M = 6. 2、 Implement the LRU algorithm for page replacement on the above access sequence. 3、 Output the page replacement sequence and the page fault rate. Essential. General Requirements 1、 For each experiment project, submit a design report and code. The code should be implemented in C++. The requirements are as follows: a) The content of the design report should include the design ideas and implementation. b) The results of the design report should include testing and running results (screenshots of screen outputs). c) The conclusion of the design report should summarize the problems encountered, the solutions and experiences during the implementation process.
06-08
<think>好的,我现在需要回答用户关于读者-写者问题的两个变种的区别,特别是在饥饿方面的正确性属性。首先,我需要回忆读者-写者问题的基本概念,以及第一和第二变种的定义。用户提到“第一读者写者问题与第二读者写者问题的区别”,所以必须明确这两个变种的具体规则。 我记得读者-写者问题主要涉及多个读者和写者访问共享资源的情况。核心在于保证数据一致性,同时允许并发读取,但写操作需要互斥。第一和第二变种的不同在于对读者和写者的优先级处理。第一类可能偏向读者,导致写者饥饿;第二类可能偏向写者,防止写者饥饿。但具体细节可能需要进一步确认。 用户特别提到饥饿问题,也就是正确性属性中的公平性。需要分析两种变种在实现时如何处理等待队列,是否会导致某些进程长期得不到资源。例如,第一变种可能让后续的读者不断进入,导致写者无法获得访问权,而第二变种可能通过限制读者或优先处理写者来避免这种情况。 接下来,我需要查阅相关的同步机制,比如信号量的使用。在读者优先的情况下,可能使用计数器来跟踪读者数量,并通过信号量控制写者的访问。而写者优先的实现可能需要更复杂的队列管理,确保一旦有写者等待,后续的读者必须等待写者完成。 可能还需要引用到死锁避免和资源分配算法,如用户提供的引用[1]中提到的安全序列和资源分配算法。不过用户的问题更侧重于饥饿,所以重点应放在调度策略和优先级管理上。 需要确保回答结构清晰,分点说明两者的定义、实现机制、饥饿情况,并举例说明。同时,注意使用正确的中文术语和LaTeX公式格式,比如信号量的操作可能需要用行内公式表示。 最后,生成相关问题时要围绕读者-写者问题的扩展,如解决方案比较、实际应用、与其他同步问题的对比等。确保问题有针对性,帮助用户深入理解。 现在整理思路,开始撰写正式回答。</think>### 读者-写者问题的定义与两类变种的差异分析 #### **读者-写者问题的核心定义** 读者-写者问题描述**多个并发进程(读者与写者)访问共享资源**时的同步需求: - **读者**:仅读取数据,允许多个读者同时访问(共享读)。 - **写者**:需修改数据,必须独占访问资源(互斥写)。 - **关键约束**:写者与读者或其他写者不能同时访问资源。 --- ### **第一读者-写者问题(读者优先)** **定义**: 只要有一个读者正在访问资源,后续所有新到达的读者均可立即进入,而写者必须等待所有读者完成。**写者可能因读者持续到达而长期等待(饥饿)**。 **实现机制**: 1. **共享计数器**:记录当前活跃读者数,通过互斥锁保护。 2. **写者信号量**:写者需等待该信号量,直到所有读者完成。 3. **典型伪代码**: ```c int readers = 0; semaphore mutex = 1; // 保护readers计数器 semaphore write_lock = 1; // 控制写者互斥 // 读者操作 void reader() { wait(mutex); readers++; if (readers == 1) wait(write_lock); // 第一个读者阻塞写者 signal(mutex); // 执行读操作 wait(mutex); readers--; if (readers == 0) signal(write_lock); // 最后一个读者释放写者 signal(mutex); } // 写者操作 void writer() { wait(write_lock); // 执行写操作 signal(write_lock); } ``` **饥饿问题**: 若读者持续到达,`write_lock`长期被占用,写者无法获取资源,导致**写者饥饿**。 --- ### **第二读者-写者问题(写者优先)** **定义**: 一旦有写者等待,后续新到达的读者必须等待所有已排队写者完成,从而**避免写者饥饿**。 **实现机制**: 1. **双重信号量**:引入优先级控制,写者等待时阻塞新读者。 2. **公平队列**:通过请求队列按顺序分配资源(如使用管程或条件变量)。 3. **典型伪代码(简化)**: ```c semaphore read_lock = 1; // 控制读者进入 semaphore write_lock = 1; // 控制写者互斥 int waiting_writers = 0; // 等待中的写者数量 semaphore priority = 1; // 写者优先信号量 // 写者操作 void writer() { wait(priority); // 获取优先权 waiting_writers++; signal(priority); wait(write_lock); // 等待写锁 // 执行写操作 signal(write_lock); wait(priority); waiting_writers--; signal(priority); } // 读者操作 void reader() { wait(priority); while (waiting_writers > 0) { // 若有写者等待,读者阻塞 wait(read_lock); } signal(priority); // 执行读操作(允许多读者进入) signal(read_lock); } ``` **饥饿问题**: 写者通过优先级信号量抢占资源,但若写者持续到达,可能导致**读者饥饿**(需额外机制平衡公平性)。 --- ### **两类问题在饥饿属性上的对比** | **特性** | 第一类(读者优先) | 第二类(写者优先) | |------------------|--------------------------------|--------------------------------| | **优先级偏向** | 读者可无限抢占资源 | 写者等待时阻塞新读者 | | **饥饿风险** | **写者可能饥饿** | **读者可能饥饿** | | **公平性** | 低(偏向读者) | 中等(偏向写者) | | **适用场景** | 读多写少,允许偶尔写延迟 | 写操作需及时响应 | --- ### **正确性属性的关键差异** 1. **安全性与活跃性**: - 第一类可能违反活跃性(写者无法保证完成)[^1]。 - 第二类通过优先级调度保证写者活跃性,但需防止读者饥饿。 2. **资源分配策略**: - 第一类采用“读者优先”的**非抢占式分配**,类似死锁避免中的安全序列检查[^1]。 - 第二类通过信号量队列实现**抢占式分配**,类似死锁检测中的资源请求比较[^1]。 --- ### **优化与平衡策略** - **公平版本**:使用**FIFO队列**,按到达顺序分配资源(如通过管程或条件变量),消除饥饿。 - **读写锁(现代实现)**:操作系统提供的读写锁(如Linux的`pthread_rwlock_t`)允许配置优先级策略。 --- ### **示例场景** 1. **读者优先**: 数据库日志分析系统,多个分析任务(读者)频繁读取日志,写操作(日志归档)低频执行。 2. **写者优先**: 实时数据采集系统,传感器数据(写者)需即时写入,数据处理任务(读者)允许短暂延迟。 --- ### **相关问题** 1. 如何设计完全公平的读者-写者解决方案? 2. 读写锁在实际操作系统(如Linux)中的实现机制? 3. 读者-写者问题与生产者-消费者问题的核心区别是什么?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值