1. 操作系统互斥与同步经典问题
- 生产者与消费者问题 ```cpp // 初始化 empty = 缓冲区大小;//生产者占用,消费者释放; full = 0;//消费者占用,生产者释放 mutex = 1; // 互斥锁, 针对缓冲区
// 生产者 while (true) { P(empty); // 减少可用空位计数 P(mutex); // 获取互斥锁 将数据放入缓冲区; // 生产数据 V(mutex); // 释放互斥锁 V(full); // 增加可用数据项计数 }
// 消费者 while (true) { P(full); // 减少可用数据项计数 P(mutex); // 获取互斥锁 从缓冲区取出数据; // 消费数据 V(mutex); // 释放互斥锁 V(empty); // 增加可用空位计数 }
2. 哲学家就餐问题
可以使用奇偶规则,即让偶数编号的哲学家「先拿左边的叉子后拿右边的叉子」,奇数编号的哲学家「先拿右边的叉子后拿左边的叉子」。
3. 读者-写者问题
这段代码是针对读者-写者问题的一个简单解决方案,使用了信号量来控制临界区的访问。在这个例子中,我们有两个信号量:`wMutex` 控制写操作的互斥,`rCountMutex` 控制对 `rCount` 的互斥修改。`rCount` 是正在进行读操作的读者个数,初始化为 0。
**简单来说**,写者只有一个写锁,读者有一个控制读计数的锁,读者读时,需要P读计数锁,如果count为0,则要写锁,防止写者修改,然后V读计数锁。读操作完成后,需要P读计数锁,如果count为0,则要释放写锁,防止写者修改,然后V读计数锁。
首先看写者进程/线程执行的函数 `writer()`:
```c
void writer()
{
while(TRUE)
{
P(wMutex); // 进入临界区
write(); // 进入临界区
V(wMutex); // 进入临界区
}
}
写者进程不断地尝试获取 wMutex
信号量,这意味着只有一个写者能进入临界区。一旦获得 wMutex
,写者就可以开始写操作,完成后释放 wMutex
,让其他写者或读者有机会访问资源。
再来看读者进程/线程执行的函数 reader()
:
void reader()
{
while(TRUE)
{
P(rCountMutex); // 进入临界区
if ( rCount == 0 )
{
P(wMutex); // 如果有写者,则阻塞写者
}
rCount++; // 读者计数 + 1
V(rCountMutex); // 离开临界区
read(); // 读数据
P(rCountMutex); // 进入临界区
rCount--; // 读完数据,准备离开
if ( rCount == 0 )
{
V(wMutex); // 最后一个读者离开了,则唤醒写者
}
V(rCountMutex); // 离开临界区
}
}
读者进程首先尝试获取 rCountMutex
信号量,然后检查 rCount
是否为零。如果是零,意味着这是第一个读者,因此需要阻塞写者(通过获取 wMutex
)。接着将 rCount
加一,表明有一个新的读者进入了临界区,然后释放 rCountMutex
并开始读取数据。
读取完毕后,再次获取 rCountMutex
,并将 rCount
减一。如果 rCount
变为零,说明这是最后一位读者,因此需要唤醒写者(通过释放 wMutex
)。最后,释放 rCountMutex
,结束本次读操作。
这个解决方案的主要思想是利用信号量来控制读者和写者的访问,确保任何时候只有一个写者能访问资源,而多个读者可以同时访问。当有写者时,读者会阻塞写者,直到所有读者都完成了读操作。