同步和条件变量

同步和条件变量

2025 南京大学《操作系统原理》
同步和条件变量

并发控制:同步 (Synchronization)

“两个或两个以上随时间变化的量在变化过程中保持一定的相对关系”

  • 同步电机、同步电路、红绿灯……

DeepSeek: 多个事件、进程或系统在时间上协调一致,确保按预定顺序同时执行

我们希望控制事件发生的先后顺序

  • ABCA \to B \to C
    • 互斥锁只能确保 “分开” A,B,CA, B, C,做不到顺序控制
  • 我们希望形成受我们控制的 “happens-before” 关系
2025 南京大学《操作系统原理》
同步和条件变量

现实世界中的同步 (1)

center

(我让 DeepSeek 找生活中的例子也给了合唱团 😊)
2025 南京大学《操作系统原理》
同步和条件变量

现实世界中的同步 (1)

理解同步:什么是事件 (代码的执行)、什么是顺序

  • 每个乐手都是一个 “线程” (状态机)
    • 事件:发出节拍、演奏节拍
    • 顺序:发出节拍 ii → 演奏节拍 ii
    • (这个模型允许一个节拍演奏得过长)
void T_player() {
    while (!end) {
        wait_next_beat();
        play_next_beat();
    }
}
2025 南京大学《操作系统原理》
同步和条件变量

现实世界中的同步 (2)

今晚 23:59:59 大活门口,不见不散!

center

2025 南京大学《操作系统原理》
同步和条件变量

现实世界中的同步 (2)

理解同步:什么是事件 (代码的执行)、什么是顺序

  • 事件 AA: 第一个人到达、事件 BB: 第二个人到达
  • 不见不散:有一个共同完成的事件 XXX\text{XXX} (类似线程 join)
    • AXXXA \to \text{XXX}, BXXXB \to \text{XXX}
      • 因此一定有一瞬间 A,BA, B 完成、XXX\text{XXX} 还没有开始
      • (同步点:一个确定的状态;从而实现并发控制)
      • “控制”:将发散的并发程序状态 “收束”
void T_A() {
    arrive_at_activity_center();  // 大活
    while (!both_A_and_B_are_here()) ;  // 不见不散
    xxx();
}
2025 南京大学《操作系统原理》
同步和条件变量

程序世界中的同步

sum = 0;  // A
spawn(T_sum);  // T_1 start -> T_1 end
spawn(T_sum);  // T_2 start -> T_2 end
join();
printf("sum = %ld\n", sum);  // B

理解同步:什么是事件 (代码的执行)、什么是顺序

  • AT1startT1endjoinBA \to T_1^{\text{start}} \to T_1^{\text{end}} \to \text{join} \to B
  • AT2startT2endjoinBA \to T_2^{\text{start}} \to T_2^{\text{end}} \to \text{join} \to B
    • 同一线程内的事件天然存在 happens-before 关系
    • 没有 happens-before 的代码就是并发
      • 在多处理器系统中可以并行执行
2025 南京大学《操作系统原理》
同步和条件变量

实现同步:实现 Happens-before

实现 ABA \to B

  • AA; can_proceed = true; (🏴)
  • while (!can_proceed) ; BB
    • BB: 如果同步的条件不满足,就等待
void T_player() {
    for (int i = 0; i < n; i++) {
        while (current_beat < i) ;
        play_beat(i);
    }
}
  • “大致可用”,但存在一些问题
    • 例如,current_beat 存在和 sum++ 类似的问题
2025 南京大学《操作系统原理》
同步和条件变量

我们不妨再问问魔法的操作系统?

能不能帮我们替代自旋的循环

while (!can_proceed) ;

“发明” 条件变量机制

  • 一个理想的 API
    • wait_until(cond),把代码编译到内核里
  • 当然,过去的操作系统设计者不会搞那么复杂
    • 条件不满足时等待 (让出 CPU)
      • wait - 直接睡眠等待
    • 条件满足时继续
      • signal/broadcast - 唤醒所有等待的线程
2025 南京大学《操作系统原理》
同步和条件变量

C++: 我可以把条件放到 λ\lambda 表达式里!

std::mutex mtx;
std::condition_variable cv;  // Since C++11

void T_player() {
    std::unique_lock lk(mtx);
    
    cv.wait(lk,
        // This reads better!
        []{ return can_proceed; }
    );

    // can_proceed holds here

    cv.notify_all();
    lk.unlock();
}
2025 南京大学《操作系统原理》