1. 读者写者问题(写者优先)
- 共享读;
- 互斥写、读写互斥;
- 写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)。
semaphore room_empty = 1; // 读写互斥
semaphore mutex_read = 1; // 保护 read_count
semaphore mutex_write = 1; // 保护 write_count
semaphore write_priority = 1; // 写者优先锁
int read_count = 0, write_count = 0;
void reader() {
P(write_priority); // 检查是否有写者等待
P(mutex_read);
read_count++;
if (read_count == 1) {
P(room_empty); // 第一个读者获取读写锁
}
V(mutex_read);
V(write_priority); // 释放写者优先锁
// 读取
P(mutex_read);
read_count--;
if (read_count == 0) {
V(room_empty); // 最后一个读者释放读写锁
}
V(mutex_read);
}
void writer() {
P(mutex_write);
write_count++;
if (write_count == 1) {
P(write_priority); // 第一个写者阻止新读者
}
V(mutex_write);
P(room_empty); // 获取读写锁
// 写入
V(room_empty);
P(mutex_write);
write_count--;
if (write_count == 0) {
V(write_priority); // 最后一个写者允许新读者
}
V(mutex_write);
}
2. 寿司店问题
假设一个寿司店有5个座位,如果你到达的时候有一个空座位,你可以立刻就坐。但是如果你到达的时候5个座位都是满的有人已经就坐,这就意味着这些人都是一起来吃饭的,那么你需要等待所有的人一起离开才能就坐。编写同步原语,实现这个场景的约束。
semaphore mutex = 1; // 保护共享变量
semaphore batch_lock = 1; // 批次锁
semaphore waiting = 0; // 等待新批次
int count = 0; // 当前批次人数
void customer() {
CONSUME:
P(mutex);
if (count == 0) {
// 新批次开始
P(batch_lock); // 获取批次锁
}
if (count < 5) {
count++;
V(mutex);
// 就坐
// 离开
P(mutex);
count--;
if (count == 0) {
// 批次结束,唤醒等待者
V(batch_lock);
for (int i = 0; i < 5; i++) {
V(waiting); // 唤醒最多5人
}
}
V(mutex);
} else {
V(mutex);
P(waiting); // 等待新批次
// 上一批次结束,被唤醒
goto CONSUME;
}
}
3. 进门问题。
(1)请给出P、V操作和信号量的物理意义。
- 信号量的物理意义:信号量是一种同步机制,用于控制多个进程/线程对共享资源的访问。可以看作是一个计数器,表示当前可用的资源数量或某种状态。
- P操作的物理意义:表示申请资源。在互斥访问中,P(S) 相当于加锁。
- V操作的物理意义:表示释放资源,如果有进程在等待该资源,则唤醒其中一个。在互斥访问中,V(S) 相当于解锁。
(2)一个软件公司有5名员工,每人刷卡上班。员工刷卡后需要等待,直到所有员工都刷卡后才能进入公司。为了避免拥挤,公司要求员工一个一个通过大门。所有员工都进入后,最后进入的员工负责关门。请用P、V操作实现员工之间的同步关系。
semaphore barrier = 0; // 等待所有员工到达
semaphore door = 1; // 控制逐个通过
semaphore mutex = 1; // 保护计数器
int count = 0;
void employee() {
P(mutex);
count++;
if (count == 5) {
V(barrier); // 第5人触发屏障
}
V(mutex);
P(barrier); // 所有员工等待在此
V(barrier); // 保持屏障信号
P(door);
// 通过大门
V(door);
P(mutex);
count--;
if (count == 0) {
// 关门
}
V(mutex);
}
4. 搜索-插入-删除问题。
三个线程对一个单链表进行并发的访问,分别进行搜索、插入和删除。搜索线程仅仅读取链表,因此多个搜索线程可以并发。插入线程把数据项插入到链表最后的位置;多个插入线程必须互斥防止同时执行插入操作。但是,一个插入线程可以和多个搜索线程并发执行。最后,删除线程可以从链表中任何一个位置删除数据。一次只能有一个删除线程执行;删除线程之间,删除线程和搜索线程,删除线程和插入线程都不能同时执行。
请编写三类线程的同步互斥代码,描述这种三路的分类互斥问题。
semaphore search_mutex = 1; // 搜索并发控制
semaphore insert_mutex = 1; // 插入互斥
semaphore delete_mutex = 1; // 删除独占
int search_count = 0;
void search() {
P(search_mutex);
search_count++;
if (search_count == 1) {
P(delete_mutex); // 阻止删除
}
V(search_mutex);
// 搜索
P(search_mutex);
search_count--;
if (search_count == 0) {
V(delete_mutex);
}
V(search_mutex);
}
void insert() {
P(insert_mutex); // 插入互斥
// 插入
V(insert_mutex);
}
void delete() {
P(delete_mutex); // 独占删除
// 删除
V(delete_mutex);
}