使用条件变量解决同步问题

使用条件变量解决同步问题

2025 南京大学《操作系统原理》
使用条件变量解决同步问题

经典同步问题:生产者-消费者问题

学废你就赢了

  • 99% 的实际并发问题都可以用生产者-消费者解决

Producer 和 Consumer 共享一个缓冲区

  • Producer (生产数据):如果缓冲区有空位,放入;否则等待
  • Consumer (消费数据):如果缓冲区有数据,取走;否则等待
    • 同步:同一个 object 的生产必须 happens-before 消费
void produce(Object obj);
Object consume();
2025 南京大学《操作系统原理》
使用条件变量解决同步问题

生产者-消费者问题的简化

一个等价的描述

void produce() { printf("("); }
void consume() { printf(")"); }
  • 生产 = 打印左括号 (push into buffer)
  • 消费 = 打印右括号 (pop from buffer)
  • 在 printf 前后增加代码,使得打印的括号序列满足
    • 不能输出错误的括号序列
    • 括号嵌套的深度不超过 nn
      • n=3n=3, ((())())((( ✅
      • n=3n=3, (((()))), (())) ❌
2025 南京大学《操作系统原理》
使用条件变量解决同步问题

条件变量的正确打开方式

想清楚程序继续执行的条件

  • 什么时候可以 produce,什么时候可以 consume?
mutex_lock(🔒);
while (!cond) {  // cond 可以是任意的计算
    cond_wait(&cv, 🔒);
}
assert(cond);    // 此时 cond 成立且持有锁 lk
mutex_unlock(🔒);
// 注意锁的使用
mutex_lock(🔒);
cond = true;
cond_broadcast(&cv); // 唤醒所有可能继续的线程
mutex_unlock(🔒);
2025 南京大学《操作系统原理》
使用条件变量解决同步问题

使用条件变量实现同步 (cont'd)

生产/消费的条件是什么?

  • d<nd < n 可以生产;d>0d > 0 可以消费 (然后,抄代码)
void produce() {
    mutex_lock(🔒);
    while (!(depth < n)) {
        cond_wait(&cv, 🔒);
    }

    assert(depth < n);
    depth++;
    printf("("); // put object to queue

    cond_broadcast(&cv);
    mutex_unlock(🔒);
}
2025 南京大学《操作系统原理》
使用条件变量解决同步问题

Caveat: 小心并发!

“看起来正确” 其实很危险

  • Producer 如果唤醒了等待的 producer 就糟了……
void produce() {
    mutex_lock(🔒);
    while (!(depth < n)) {
        cond_wait(&cv, 🔒);
    }

    assert(depth < n);
    depth++;
    printf("("); // put object to queue

    cond_signal(&cv);  // ⚠
    mutex_unlock(🔒);
}
2025 南京大学《操作系统原理》
使用条件变量解决同步问题

条件变量:万能的同步方法

有三种线程

  • TaT_a 若干: 死循环打印 <
  • TbT_b 若干: 死循环打印 >
  • TcT_c 若干: 死循环打印 _

任务:

  • 对线程同步,使得屏幕打印出 <><_><>_ 的组合

使用条件变量,只要回答三个问题:

  • 打印 “<” 的条件?打印 “>” 的条件?打印 “_” 的条件?
2025 南京大学《操作系统原理》