并发控制:同步(条件变量、信号量、生产者消费者)(一)

69 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情 

同步 (Synchronization)

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

  • iPhone/iCloud 同步 (手机 vs 电脑 vs 云端)
  • 变速箱同步器 (合并快慢速齿轮)
  • 同步电机 (转子与磁场速度一致)
  • 同步电路 (所有触发器在边沿同时触发)

异步 (Asynchronous) = 不同步

  • 上述很多例子都有异步版本 (异步电机、异步并发程序中的同步

并发程序的步调很难保持 “完全一致”

线程同步:

在某个时间点共同达到互相已知的状态


再次把线程想象成我们自己

  • NPY:等我洗个头就出门/等我打完这局游戏就来
  • 舍友:等我修好这个 bug 就吃饭
  • 导师:等我出差回来就讨论这个课题
  • jyy: 等我成为卷王就躺平
    • “先到先等”电路、异步线程)

线程同步就是两个线程约定好一个时间达到互相知道的状态。

生产者-消费者问题:

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

void Tproduce() { while (1) printf("("); } void Tconsume() { while (1) printf(")"); }

在 printf 前后增加代码,使得打印的括号序列满足

  • 一定是某个合法括号序列的前缀
  • 括号嵌套的深度不超过n
  • 当n等于3时
  • ((())())((( 合法
  • (((()))), (())) 不合法
  • 同步
    • 等到有空位再打印左括号
    • 等到能配对时再打印右括号

生产者-消费者问题:分析

为什么叫 “生产者-消费者” 而不是 “括号问题”?

  • 左括号:生产资源 (任务)、放入队列
  • 右括号:从队列取出资源 (任务) 执行

能否用互斥锁实现括号问题?

  • 左括号:嵌套深度 (队列) 不足 n 时才能打印
  • 右括号:嵌套深度 (队列) >1 时才能打印
    • 当然是等到满足条件时再打印了:pc.c
      • 用互斥锁保持条件成立

#include "thread.h" #include "thread-sync.h" int n, count = 0; mutex_t lk = MUTEX_INIT(); void Tproduce() {//老师(生产者) while (1) { retry: mutex_lock(&lk); if (count == n) { mutex_unlock(&lk);//释放锁 goto retry; } count++;//获取锁后的操作 printf("("); mutex_unlock(&lk); } } void Tconsume() {//学生(消费者) while (1) { retry: mutex_lock(&lk); if (count == 0) { mutex_unlock(&lk); goto retry; } count--; printf(")"); mutex_unlock(&lk); } } int main(int argc, char *argv[]) { assert(argc == 2); n = atoi(argv[1]); setbuf(stdout, NULL); for (int i = 0; i < 8; i++) { create(Tproduce); create(Tconsume); } }