同步示例解析
立即解锁
发布时间: 2025-08-14 00:50:59 阅读量: 3 订阅数: 18 


操作系统概念:理论与实践
### 同步示例解析
#### 1. 经典同步问题概述
在并发编程中,存在许多同步问题,这些问题被广泛用于测试新的同步方案。下面将介绍几个经典的同步问题。
#### 2. 有界缓冲区问题
有界缓冲区问题常被用于展示同步原语的强大功能。在这个问题中,生产者和消费者进程共享以下数据结构:
```plaintext
int n;
semaphore mutex = 1;
semaphore empty = n;
semaphore full = 0
```
这里假设缓冲区池由 `n` 个缓冲区组成,每个缓冲区能容纳一个项目。`mutex` 二元信号量用于对缓冲区池的访问提供互斥,初始值为 1。`empty` 和 `full` 信号量分别统计空缓冲区和满缓冲区的数量,`empty` 初始值为 `n`,`full` 初始值为 0。
生产者进程的代码如下:
```plaintext
while (true) {
...
/* produce an item in next produced */
...
wait(empty);
wait(mutex);
...
/* add next produced to the buffer */
...
signal(mutex);
signal(full);
}
```
消费者进程的代码如下:
```plaintext
while (true) {
wait(full);
wait(mutex);
...
/* remove an item from buffer to next consumed */
...
signal(mutex);
signal(empty);
...
/* consume the item in next consumed */
...
}
```
可以看到生产者和消费者的代码具有对称性,生产者为消费者生产满缓冲区,消费者为生产者生产空缓冲区。
#### 3. 读者 - 写者问题
假设有一个数据库要被多个并发进程共享,有些进程只需要读取数据库(读者),而有些进程需要更新数据库(写者)。如果两个读者同时访问共享数据,不会产生不良影响,但如果写者和其他进程(读者或写者)同时访问数据库,可能会导致混乱。
读者 - 写者问题有多种变体,主要涉及优先级。第一种读者 - 写者问题要求,除非写者已经获得使用共享对象的权限,否则不应让读者等待。第二种读者 - 写者问题要求,一旦写者准备好,应尽快执行写操作。
解决这两个问题都可能导致饥饿现象。下面给出第一种读者 - 写者问题的解决方案,读者进程共享以下数据结构:
```plaintext
semaphore rw mutex = 1;
semaphore mutex = 1;
int read count = 0;
```
`mutex` 和 `rw mutex` 二元信号量初始值为 1,`read count` 计数信号量初始值为 0。`rw mutex` 信号量供读者和写者进程共同使用,`mutex` 信号量用于在更新 `read count` 变量时确保互斥。
写者进程的代码如下:
```plaintext
while (true) {
wait(rw mutex);
...
/* writing is performed */
...
signal(rw mutex);
}
```
读者进程的代码如下:
```plaintext
while (true) {
wait(mutex);
read count++;
if (read count == 1)
wait(rw mutex);
signal(mutex);
...
/* reading is performed */
...
wait(mutex);
read count--;
if (read count == 0)
signal(rw mutex);
signal(mutex);
}
```
读者 - 写者问题的解决方案在一些系统中被推广为读者 - 写者锁。当进程只需要读取共享数据时,以读模式请求锁;当进程需要修改共享数据时,以写模式请求锁。多个进程可以同时以读模式获取锁,但写操作需要独占访问。
读者 - 写者锁在以下情况最有用:
- 在容易区分哪些进程只读取共享数据,哪些进程只写入共享数据的应用中。
- 在读者多于写者的应用中。因为读者 - 写者锁通常比信号量或互斥锁需要更多的开销来建立,但允许多个读者同时访问带来的并发增加可以补偿设置锁的开销。
#### 4. 哲学家就餐问题
有五位哲学家围坐在圆桌旁,中间放着一碗米饭,桌上有五根筷子。哲学家要么思考,要么吃饭。当哲学家饥饿时,会尝试拿起左右两边的筷子,只有同时拿到两根筷子才能吃饭,吃完后放下筷子继续思考。
这个问题是经典的同步问题,它代表了一大类并发控制问题,即如何在无死锁和无饥饿的情况下为多个进程分配多个资源。
##### 4.1 信号量解决方案
一种简单的解决方案是用信号量表示每根筷子。哲学家通过对信号量执行 `wait()` 操作来拿起筷子,通过执行 `signal()` 操作来放下筷子。共享数据如下:
```plaintext
semaphore chopstick[5];
```
其中 `chopstick` 的所有元素初始值为 1。哲学家 `i` 的代码如下:
```plaintext
while (true) {
wait(chopstick[i]);
wait(chopstick[(i+1) % 5]);
...
/* eat
```
0
0
复制全文
相关推荐









