2.5 经典经常的同步问题

 

 

2.5 经典进程同步问题

进程同步问题是多道程序环境下的一个重要概念,涉及多个进程对共享资源的访问控制。其中,“生产者-消费者问题”、“读者-写者问题”和“哲学家进餐问题”是进程同步的经典问题,通过它们可以深入理解进程同步的概念和实现方法。

2.5.1 生产者-消费者问题

生产者-消费者问题描述了两类进程—生产者和消费者—共享一个固定大小的缓冲区。生产者产生数据并放入缓冲区,消费者从缓冲区取出数据进行消费。该问题的核心是确保生产者不会在缓冲区满时向其添加数据,消费者不会在缓冲区空时从中取数据,以及对缓冲区的互斥访问。

利用记录型信号量解决生产者-消费者问题
  • 信号量 mutex 用于保证对缓冲区的互斥访问
  • 信号量 empty 表示空缓冲区的数量,初始化为缓冲区的大小
  • 信号量 full 表示满缓冲区的数量,初始化为0

生产者进程在添加数据之前,先通过 wait(empty) 检查是否有空缓冲区,然后通过 wait(mutex) 进入临界区。消费者进程在取出数据之前,先通过 wait(full) 检查是否有数据,然后通过 wait(mutex) 进入临界区。这样,可以有效地同步生产者和消费者对缓冲区的访问。

利用AND信号量解决生产者-消费者问题

AND信号量是一种高级同步机制,允许进程在一组条件同时满足时才继续执行。在生产者-消费者问题中,可以使用AND信号量同时检查缓冲区是否有空位和是否互斥,从而进一步简化信号量的使用。

利用管程解决生产者-消费者问题

管程提供了一种封装共享资源和同步机制的方法,可以更加直观和安全地解决生产者-消费者问题。在管程内部,可以定义条件变量来控制生产者和消费者的等待与唤醒,使代码结构更加清晰。

  • 管程内部的共享数据结构表示缓冲区
  • 条件变量 notfullnotempty 分别用于控制缓冲区不满和不空的情况
  • 管程提供 putget 方法供生产者和消费者调用,以实现对缓冲区的安全访问

通过这些方法,我们可以看到进程同步问题的多种解决方案,每种方案都有其适用场景和特点。理解这些经典问题及其解决方案有助于深入学习操作系统中进程同步和互斥的核心概念。

 

2.5.2 哲学家进餐问题

哲学家进餐问题是一个经典的进程同步问题,用于描述进程同步和互斥的复杂性。问题设置五位哲学家围坐在一张圆桌旁,交替地进行思考和进餐。进餐时需要使用两只筷子,筷子放在哲学家之间。每位哲学家要进餐时,必须同时拿起左右两边的筷子,进餐完毕后再放下筷子,继续思考。

利用记录型信号量解决哲学家进餐问题

通过为每只筷子分配一个信号量,并初始化为1(表示筷子可用),哲学家进餐问题的解决方案可以描述如下:

semaphore chopstick[5] = {1, 1, 1, 1, 1};

void philosopher(int i) {
    do {
        wait(chopstick[i]); // 尝试拿起左边的筷子
        wait(chopstick[(i+1) % 5]); // 尝试拿起右边的筷子
        // 进餐
        signal(chopstick[i]); // 放下左边的筷子
        signal(chopstick[(i+1) % 5]); // 放下右边的筷子
        // 思考
    } while(TRUE);
}

这种方法虽然能防止相邻哲学家同时进餐,但可能导致死锁,即所有哲学家都拿起了左边的筷子,等待右边的筷子。

解决死锁问题的策略
  1. 限制同时饥饿的哲学家数量:确保至少有一位哲学家可以同时拿到两只筷子进餐。
  2. 同时拿起两只筷子:哲学家只有在两边的筷子都可用时才能拿起筷子。
  3. 改变拿筷子的顺序:规定奇数号哲学家先拿左边的筷子,偶数号哲学家先拿右边的筷子,或反之,以避免循环等待的产生。
利用AND信号量机制解决哲学家进餐问题

AND信号量机制可以确保哲学家必须同时获得两只筷子才能进餐,这是一种简洁的解决方案,避免了复杂的死锁问题:

semaphore chopstick[5] = {1, 1, 1, 1, 1};

void philosopher(int i) {
    do {
        // 思考
        Swait(chopstick[(i+1) % 5], chopstick[i]); // 同时等待两只筷子
        // 进餐
        Ssignal(chopstick[(i+1) % 5], chopstick[i]); // 同时释放两只筷子
    } while(TRUE);
}

通过这些方法,哲学家进餐问题不仅揭示了进程同步和互斥的复杂性,也展示了如何通过同步机制有效解决多进程共享资源的问题。这些解决方案有助于深入理解操作系统中的进程同步机制,以及如何在实际应用中避免死锁和资源竞争的问题。

2.5.3 读者-写者问题

读者-写者问题是操作系统中一个经典的进程同步问题,用于描述读操作和写操作对共享资源访问控制的同步机制。问题的核心是允许多个读者同时读取共享资源,但写者进行写操作时必须独占资源,且写操作与读操作也不能同时进行。

1. 利用记录型信号量解决读者-写者问题

为了实现读者与写者之间的互斥访问共享资源,通常采用以下信号量和变量:

  • rmutex(读互斥信号量):用于保证readcount变量的互斥访问。
  • wmutex(写互斥信号量):用于实现写者对共享资源的独占访问。
  • readcount:记录当前正在读取共享资源的读者数量。

读者进程首先通过wait(rmutex)进入临界区修改readcount。如果是第一个读者,则需要通过wait(wmutex)获取资源的读权限。完成读操作后,读者再次通过wait(rmutex)进入临界区更新readcount,如果是最后一个读者,则通过signal(wmutex)释放资源的读权限。

写者进程通过wait(wmutex)获取资源的独占访问权限,完成写操作后通过signal(wmutex)释放权限。

2. 利用信号量集机制解决读者-写者问题

当需要对同时读取的读者数量进行限制时,可以引入另一个信号量L和整数RN来实现,其中RN表示最大允许的同时读者数。读者在尝试读取资源前,需要通过wait(L, 1, 1)来确保不超过RN的限制。写者在尝试写入资源前,需要确保没有读者在读取资源且自己能获取写权限。

// 读者进程
void Reader() {
    do {
        Swait(L, 1, 1); // 限制读者数量
        Swait(mx, 1, 0); // 检查是否可以读取
        // 执行读操作
        Ssignal(L, 1); // 完成读操作,释放一个读者位置
    } while(TRUE);
}

// 写者进程
void Writer() {
    do {
        Swait(mx, 1, 1, L, RN, 0); // 确保无读者且获取写权限
        // 执行写操作
        Ssignal(mx, 1); // 完成写操作,释放写权限
    } while(TRUE);
}

这种方法通过信号量集机制有效控制了读者和写者对共享资源的访问,同时满足了对读者数量的限制要求。此解法保证了读者与写者间的公平性和资源访问的互斥性,是处理读者-写者问题的一种高效方案。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏驰和徐策

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值