复习:生产者-消费者、互斥、条件变量

打印 “合法” 的括号序列 (())()

  • 左括号对应 push
  • 右括号对应 pop
#define CAN_PRODUCE (count < n)
#define CAN_CONSUME (count > 0)

wait_until(CAN_PRODUCE) with (mutex) {
  count++;
  printf("(");
}

wait_until(CAN_CONSUME) with (mutex) {
  count--;
  printf(")");
}

信号量:一种条件变量的特例

void P(sem_t *sem) { // wait
  wait_until(sem->count > 0) {
    sem->count--;
  }
}

void V(sem_t *sem) { // post (signal)
  atomic {
    sem->count++;
  }
}

正是因为条件的特殊性,信号量不需要 broadcast

  • P 失败时立即睡眠等待
  • 执行 V 时,唤醒任意等待的线程

理解信号量 (1)

初始时 count = 1 的特殊情况

  • 互斥锁是信号量的特例

#define YES 1
#define NO 0

void lock() {
  wait_until(count == YES) {
    count = NO;
  }
}

void unlock() {
  count = YES;
}

理解信号量 (2)

P - prolaag (try + decrease/down/wait/acquire)

  • 试着从袋子里取一个球
    • 如果拿到了,离开
    • 如果袋子空了,排队等待

V - verhoog (increase/up/post/signal/release)

  • 往袋子里放一个球
    • 如果有人在等球,他就可以拿走刚放进去的球了
    • 放球-拿球的过程实现了同步

理解信号量 (3)

扩展的互斥锁:一个手环 → $n$ 个手环

  • 让更多同学可以进入更衣室
    • 管理员可以持有任意数量的手环 (count, 更衣室容量上限)
    • 先进入更衣室的同学先进入游泳池
    • 手环用完后需要等同学出来
  • 信号量对应了 “资源数量”

v.s.

信号量:实现优雅的生产者-消费者

信号量设计的重点

  • 考虑 “球”/“手环” (每一单位的 “资源”) 是什么
  • 生产者/消费者 = 把球从一个袋子里放到另一个袋子里
void Tproduce() {
  P(&empty);
  printf("("); // 注意共享数据结构访问需互斥
  V(&fill);
}
void Tconsume() {
  P(&fill);
  printf(")");
  V(&empty);
}