gem-universe / blog

0 stars 0 forks source link

[操作系统]10. 并发控制-同步(信号量与哲学家吃饭问题) #10

Open supergem3000 opened 9 months ago

supergem3000 commented 9 months ago

10. 并发控制:同步 (2) (jyywiki.cn)

信号量

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


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

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

> 初始时count=1的特殊情况:互斥锁是信号量的特例
> ```c
> #define YES 1
> #define NO 0
> 
> void lock() {
>   wait_until(count == YES) {
>     count = NO;
>   }
> }
> 
> void unlock() {
>   count = YES;
> }
> ```
>
>扩展的互斥锁:一个手环->n个手环
>让更多同学可以进入更衣室:管理员可以持有任意数量的手环 (count, 更衣室容量上限)。先进入更衣室的同学先进入游泳池。手环用完后需要等同学出来。
>信号量对应了 “资源数量”

- P - prolaag (try + decrease/down/wait/acquire)
    试着从袋子里取一个球。如果拿到了,离开。如果袋子空了,排队等待
- V - verhoog (increase/up/post/signal/release)
    往袋子里放一个球。如果有人在等球,他就可以拿走刚放进去的球了
    放球-拿球的过程实现了同步

优雅地实现生产者-消费者
```c
void Tproduce() {
  P(&empty);
  printf("("); // 注意共享数据结构访问需互斥
  V(&fill);
}
void Tconsume() {
  P(&fill);
  printf(")");
  V(&empty);
}

理解:生产者有一个口袋,放n个球。消费者有一个口袋,初始没有球。打印一个左括号,把一个球从生产者的口袋转移到消费者的口袋;打印一个右括号,把一个球从消费者的口袋转移到生产者的口袋。

信号量:应用

实现一次临时的 happens-before

此场景比条件变量简易。用条件变量,A做完后broadcast,如果此时B对应的线程还没有,则broadcast就丢了,还需要做额外的什么什么条件来告诉B。

实现计数类型的同步

上边两种应用对应两种线程join的方法 T1->T2->... VS 完成就行不管顺序

实现计算图

a ---> c ---> d ---> e
|             ↑
|             |
⨽----> b ----⨼

哲学家吃饭问题

经典同步问题:5个哲学家在圆桌围成一桌,(线程)有时思考,有时吃饭。吃饭需要同时得到左手和右手的叉子。当叉子被其他人占有时,必须等待,如何完成同步?

失败的尝试:把信号量当互斥锁,先拿一把叉子,再拿另一把叉子。

void Tphilosopher() {
  while (1) {
    P(lhs);
    P(rhs);
    eat();
    V(lhs);
    V(rhs);
  }
}

成功的尝试(万能方法,条件变量)

#define CAN_EAT (avail[lhs] && avail[rhs])
mutex_lock(&mutex);
while (!CAN_EAT)
  cond_wait(&cv, &mutex);
avail[lhs] = avail[rhs] = false;
mutex_unlock(&mutex);

mutex_lock(&mutex);
avail[lhs] = avail[rhs] = true;
cond_broadcast(&cv);
mutex_unlock(&mutex);

成功的尝试:信号量。 死锁会在5个哲学家“同时吃饭”时发生。破坏这个条件即可,保证任何时候至多只有4个人可以吃饭。

void Tphilosopher() {
  while (1) {
    P(table);

    P(lhs);
    P(rhs);
    eat();
    V(lhs);
    V(rhs);

    V(table);
  }
}

int main() {
  SEM_INIT(table, 4);
  // ......
}

反思:分布与集中 “Leader/follower (master/slave)” - 有一个集中的“总控”,而非“各自协调”。 哲学家自己不决定什么时候吃饭。由服务员进行调度。 联想:分布式存储 master 的角色。