软考(八)进程同步

进程管理(三)进程同步

基本概念:

进程同步概念:

进程同步指的是协调多个并发执行的进程或线程之间的操作顺序,以确保它们按照既定的方式交互和共享资源。进程同步的目的是防止竞争条件(Race Condition)、死锁(Deadlock)、活锁(Livelihood)、饥饿(Starvation)等并发编程中常见的问题,确保系统的正确性、可靠性和稳定性

eg:假设计算2+5*6,系统产生了2个进程: 一个加法,一个乘法;为了保证计算正确,则必须需要先做乘法进程,之后再处理加法进程。所以需要一定的机制约束加法进程要后执行

临界区:

多个进程可以共享系统资源,但是有许多资源一次只能够给一个进程所用,将一次仅仅允许一个进程使用的资源称为临界资源。如设备(打印机等),内存,文件,变量等。临界区的目的就是确保任何时刻,只有一个线程或进程可以进入临界区执行(互斥的访问),以避免竞态条件(Race Condition)和数据不一致性等并发问题

临界区访问过程:

  1. 进入区:为了进入临界区使用临界资源,在进入区要检查可否进入临界区,若能进入临界区,应设置正在访问临界区的标志,以阻止其他进程同时进入临界区
  2. 临界区:进程中访问临界资源的那段代码
  3. 退出区:将正在访问临界区的标志清除
  4. 剩余区:代码中剩余的部分

同步:

也称为制约关系,指为了完成某任务而建立的两个或多个进程,这些进程需要在某些位置上协调它们的工作次序而等待、传递信息所产生的制约关系

eg:输入进程A通过单缓冲 向进程B提供数据,当该缓冲区空,进程B则不能获取数据而阻塞。 当进程A将数据送入缓冲区时,进程B就被唤醒。反之,当缓冲区满时,进程A被阻塞,仅当进程B取走缓冲区时,才唤醒进程A

互斥:

称为间接制约关系,当一个进程进入临界区使用临界资源时,另外一个进程必须等待,当临界资源的进程退出临界区后,另外一个进程才运行访问该临界资源

信号量

原语: 原语指的是 完成某种功能且不被分割、不被中断执行的操作序列,通常可由硬件来实现。

信号量: 信号量机制是一种用于控制对共享资源的访问的同步机制,常用于多线程或多进程的并发编程中。信号量维护一个整型计数器和一个等待队列,用于实现对临界资源的互斥访问和线程间的协调通信。

信号量的基本操作包括两个原子操作:wait(等待)(P操作)和 post(释放)(V操作)

  1. wait 操作(也称为 P 操作):当一个线程或进程希望访问临界资源时,首先执行 wait 操作。如果信号量的计数器大于零,则将计数器减一,并允许线程继续执行;否则,线程会被阻塞,直到计数器变为大于零为止
  2. post 操作(也称为 V 操作):当一个线程或进程使用完临界资源后,执行 post 操作来释放资源。该操作会将信号量的计数器加一,并唤醒等待队列中的一个线程,使其可以继续执行

二元信号量的计数器只能取 0 或 1,用于表示资源的空闲或占用状态。常用于实现互斥锁的功能

计数信号量的计数器可以取任意非负整数,用于表示可用资源的数量。常用于限制并发访问的数量或者表示资源池的状态

**管程:**管程(Monitor)是一种用于同步并发程序的抽象数据类型,提供了一种机制来限制对共享资源的访问,以确保线程或进程间的协作和互斥

管程的组成:

  1. 共享数据结构(Shared Data Structure): 管程中包含了需要被多个线程或进程共享的数据结构,这些数据结构可能包含了共享资源或者用于线程间通信的缓冲区
  2. 操作(Operations): 管程中定义了一系列操作或函数,用于对共享数据进行访问和操作。这些操作可能包括获取资源、释放资源、等待条件、唤醒等待的线程等
  3. 条件变量(Condition Variables): 管程中通常包含了一个或多个条件变量,用于实现线程间的等待和唤醒机制。条件变量可以用来表示一些特定的条件,当条件不满足时,线程可以等待在条件变量上,直到其他线程发出信号唤醒它
  4. 互斥锁(Mutex): 为了确保对共享资源的互斥访问,管程通常包含了一个互斥锁,用于控制多个线程对共享资源的访问。只有持有互斥锁的线程才能访问共享资源,其他线程需要等待锁的释放

经典同步问题

生产者 - 消费者问题

问题简述:

  1. 存在一个共享的有限缓冲区,生产者和消费者共享该缓冲区
  2. 生产者的任务是生产产品,并将其放入缓冲区、
  3. 消费者的任务是从缓冲区中取出产品并进行消费
  4. 缓冲区为空时,消费者必须等待,直到有产品可用
  5. 缓冲区已满时,生产者必须等待,直到有空间可用
  6. 生产者和消费者之间的操作必须同步,以避免竞争条件和数据不一致性

生产者-消费者问题的关键在于如何实现生产者和消费者之间的协调,以及对共享资源的互斥访问

需要信号量、管程、条件变量等同步机制来确保生产者和消费者的正确操作

buffer: array of length N  // 共享缓冲区
in, out: integer           // 缓冲区的读写指针

semaphore mutex = 1        // 互斥锁,用于对缓冲区的访问进行互斥
semaphore fullSlots = 0    // 表示已满的槽位数量,初始为0
semaphore emptySlots = N   // 表示空闲的槽位数量,初始为缓冲区大小N

procedure producer:
    while true:
        item = produceItem()  // 生产物品
        wait(emptySlots)      // 等待有空闲槽位可用
        wait(mutex)           // 获取对缓冲区的互斥访问
        buffer[in] = item     // 将物品放入缓冲区
        in = (in + 1) % N     // 更新写指针
        signal(mutex)         // 释放对缓冲区的互斥访问
        signal(fullSlots)     // 增加已满的槽位数量

procedure consumer:
    while true:
        wait(fullSlots)        // 等待有产品可消费
        wait(mutex)            // 获取对缓冲区的互斥访问
        item = buffer[out]     // 从缓冲区取出物品
        out = (out + 1) % N    // 更新读指针
        signal(mutex)          // 释放对缓冲区的互斥访问
        signal(emptySlots)     // 增加空闲的槽位数量
        consumeItem(item)      // 消费物品

  1. mutex 信号量用于实现对共享缓冲区的互斥访问。在生产者和消费者操作缓冲区时,必须先获取 mutex 信号量,以确保同一时刻只有一个线程可以访问缓冲区,避免数据竞争和不一致性
  2. fullSlotsemptySlots 信号量分别用于表示已满的槽位数量和空闲的槽位数量。生产者在生产物品时,必须等待至少有一个空闲槽位可用,而消费者在消费物品时,必须等待至少有一个已满的槽位可用
  3. 在生产者生产物品和消费者消费物品之后,分别通过 signal(fullSlots)signal(emptySlots) 来更新已满和空闲槽位的数量,以通知等待的生产者和消费者可以继续操作
读者 - 写者问题

问题简述:

  1. 存在一个共享的数据结构(如一个文件、数据库等),可以被多个读者同时访问,但只允许一个写者独占访问
  2. 读者的任务是从共享资源中读取数据,多个读者可以同时访问共享资源,且读者之间不会相互影响
  3. 写者的任务是向共享资源中写入数据,写者需要独占地访问共享资源,且不允许同时存在多个写者或读者和写者同时访问
  4. 当有写者在对共享资源进行写操作时,不允许其他任何读者或写者访问共享资源
  5. 当有读者在对共享资源进行读操作时,其他读者可以同时访问共享资源,但不允许写者访问共享资源
  6. 读者和写者之间的操作必须同步,以避免竞态条件和数据不一致性
mutex: semaphore = 1         // 用于对读者数量和写者访问的互斥
writeInProgress: semaphore = 0  // 表示是否有写者正在访问
readers: integer = 0         // 表示当前正在访问共享资源的读者数量

procedure reader:
    wait(mutex)              // 获取对读者数量的互斥访问
    readers = readers + 1    // 增加读者数量
    if readers == 1:
        wait(writeInProgress) // 如果是第一个读者,则等待写者结束
    signal(mutex)            // 释放对读者数量的互斥访问

    // 读取共享资源
    read_shared_data()

    wait(mutex)              // 获取对读者数量的互斥访问
    readers = readers - 1    // 减少读者数量
    if readers == 0:
        signal(writeInProgress) // 如果没有读者了,唤醒可能等待的写者
    signal(mutex)            // 释放对读者数量的互斥访问

procedure writer:
    wait(mutex)              // 获取对写者访问的互斥
    if readers > 0 or writeInProgress == 1:
        signal(mutex)        // 如果有读者或者其他写者正在访问,则等待
        wait(writeInProgress) // 等待当前写者结束
    writeInProgress = 1      // 表示当前有写者正在访问
    signal(mutex)            // 释放对写者访问的互斥

    // 写入共享资源
    write_shared_data()

    writeInProgress = 0      // 写者结束访问
    signal(writeInProgress)  // 唤醒其他等待的写者或读者

哲学家进餐问题

问题的描述:

  1. 有五位哲学家围坐在一张圆桌周围,每位哲学家面前都有一碗意大利面
  2. 每位哲学家都需要两把餐叉才能进餐,一把放在左边,一把放在右边
  3. 哲学家的生活包括两个活动:思考和进餐。当哲学家饿了时,他试图进餐,此时他会先尝试拿起他左右两边的餐叉
  4. 如果他拿到了两把餐叉,他就会进餐一段时间,然后把餐叉放回原处继续思考
  5. 如果他无法同时拿到两把餐叉,他就会放下已经拿到的餐叉,继续思考等待
  6. 问题的关键在于如何确保每位哲学家都能有机会进餐,并且避免死锁(即每位哲学家都拿起了一只餐叉,但无法再拿到另一只导致永远无法进餐)

在这里插入图片描述

const int N = 5; // 哲学家数量
enum { THINKING, HUNGRY, EATING } state[N]; // 每位哲学家的状态

semaphore mutex = 1; // 用于对临界区的互斥访问
semaphore S[N]; // 用于表示每位哲学家是否可以进餐的信号量数组

void philosopher(int i) {
    while (true) {
        think(); // 哲学家思考
        pickup_forks(i); // 哲学家拿起叉子
        eat(); // 哲学家进餐
        putdown_forks(i); // 哲学家放下叉子
    }
}

void pickup_forks(int i) {
    wait(mutex); // 进入临界区
    state[i] = HUNGRY; // 设置哲学家状态为饥饿
    test(i); // 尝试拿叉子
    signal(mutex); // 离开临界区
    wait(S[i]); // 如果拿不到叉子,则等待
}

void putdown_forks(int i) {
    wait(mutex); // 进入临界区
    state[i] = THINKING; // 设置哲学家状态为思考
    test((i + N - 1) % N); // 检查左邻居是否可以进餐
    test((i + 1) % N); // 检查右邻居是否可以进餐
    signal(mutex); // 离开临界区
}

void test(int i) {
    if (state[i] == HUNGRY && // 如果当前哲学家饥饿
        state[(i + N - 1) % N] != EATING && // 左邻居不在进餐
        state[(i + 1) % N] != EATING) { // 右邻居不在进餐
        state[i] = EATING; // 哲学家可以进餐
        signal(S[i]); // 唤醒哲学家
    }
}


  • 22
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值