生产者-消费者问题
要求
- 生成者在缓存未满时,可添加数据;
- 消费者在缓存未空时,可消费数据;
- 缓存同一时刻只有一方在操作。
基本思路
- 添加empty信号量,表示缓存未满;
- 添加full信号量,表示缓存未空;
- 添加mutex互斥量,保证只有一方操作。
代码
#define N 100
typedef int semaphore
semaphore empty = N; // 大小为缓存大小, 可理解为缓存空余大小
semaphore full = 0; // 初始化为0,保证消费者不会直接执行,可理解为缓存使用大小
semaphore mutex = 1;
void produce()
{
while(TRUE)
{
int data = produceData();
down(&empty); // 先确定是否可操作
down(&mutex); // 然后在通过互斥量操作,避免死锁
insert(item);
up(&mutex);
up(&full);
}
}
void consumer()
{
while(TRUE)
{
down(&full);
down(&mutex);
int data = removeData();
consumeData(data);
up(&mutex);
up(&empty);
}
}
* 上述代码引自CyC2018
读写问题
要求
- 读写,写写不能同时发生;
- 读读之间可以同时发生。
基本思路
- 引入资源锁,当首次读出现时,加资源锁,当读全部结束时,解资源锁;
- 引入读数量,确定什么时候加(解)资源锁,对于读数量的改变也要在临界区中进行。
- 写数据直接,获取资源锁 -> 写操作 -> 释放资源锁。
代码
#define int semaphore;
semaphore count_mutex = 1;
semaphore data_mutex = 1;
int count = 0;
void reader()
{
while (TRUE)
{
down(&count_mutex);
count++;
if (count == 1) { // 第一个读锁住资源,避免写
down(&data_mutex);
}
up(&count_mutex);
read();
down(&count_mutex);
count--;
if (count == 0) { // 最后一个读解锁资源
up(&data_mutex);
}
up(&count_mutex);
}
}
void writer()
{
while(TRUE)
{
down(&data_mutex);
write();
up(&data_mutex);
}
}
* 上述代码引自CyC2018
注意:上述方法若是读太多会导致写饿死。此时可以加入队列来解决上述情况,即在读写之间尝试获取队列互斥量,写线程获取了队列信号量之后,读线程获取不到,就不会继续添加 count 了。
哲学家问题
要求
- 哲学家有思考和吃饭两个状态,吃饭状态需要两个筷子,即必须获取到左右两边的筷子;
- 每个哲学家之间只有一根筷子;
基本思路
- 为了避免死锁,每次吃之前都要获取两个筷子,吃完要放下两个筷子,不能单个筷子操作,两个筷子的取放视为原子操作;
- 只有在两边都没有进餐的时候,才可以进餐;
- 将哲学家分为三个状态,思考状态不需要筷子,进餐状态获取了左右的筷子,饥饿状态表示等待,等待获取筷子。
代码
#define N 5
#define LEFT (i + N - 1) % N
#define RIGHT (i + 1) % N
#define THINK 0
#define EAT 2
#define HUNGRY 3
#define int semaphore;
semaphore state_mutex = 1; // 状态临界区互斥量
int state[N] = {0}; // 哲学家状态
semaphore state_mutex[N] // 每个哲学家一个信号量用作等待
void philosopher(int i)
{
while(TRUE)
{
think(i);
take_two(i);
eat(i);
put_two(i);
}
}
void take_two(int i)
{
down(&state_mutex); // 准备修改状态
state[i] = HUNGRY; // 将状态设置为饥饿
tryEat(i); // 尝试获取筷子
up(&state_mutex);
down(&state_mutex[i]); // 若获取成功,不会阻塞,失败则阻塞
}
void put_two(int i)
{
down(&state_mutex); // 准备修改状态
state[i] = THINK; // 将状态设置为思考
tryEat(LEFT); // 让左边的人尝试获取筷子
tryEat(RIGHT); // 让右边的人尝试获取筷子
up(&state_mutex);
}
void tryEat(int i)
{
if (state[i] == HUNGRY && state[LEFT] != EAT&& state[RIGHT] != EAT)
{
state[i] = EAT;
up(&state[i]);
}
}
* 上述代码引自CyC2018