当前位置: 首页 > news >正文

《计算机操作系统》_并发控制:同步(条件变量、信号量、生产者消费者、哲学家吃饭问题)20251104

        ok煮波躺了半个月,好多课程学不会,好多作业、好多pre,赶一起了,实在没时间更新。ok今晚没课,更新一篇。

        同步(Asynchronous):多个变量在变化过程中保持一定的相对关系

        异步(不同步)

        1.并发程序的同步

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

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

1.1生产者-消费者问题

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

合法括号问题:

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

左括号:生产资源(任务)、放入队列。

右括号:从队列中取出资源(任务)执行。

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

#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) {                       //如果深度达到n,那么就解开锁,开始循环mutex_unlock(&lk);goto retry;}count++;                                //一旦条件不成立,打印左括号,深度+1printf("(");mutex_unlock(&lk);}
}void Tconsume() {while (1) {
retry:mutex_lock(&lk);                        //如果深度为0,不可以打印右括号,开始循环if (count == 0) {mutex_unlock(&lk);goto retry;}count--;                                //条件不成立的话,可以打印右括号,深度-1printf(")");mutex_unlock(&lk);}
}int main(int argc, char *argv[]) {           //n的深度选择assert(argc == 2);n = atoi(argv[1]);setbuf(stdout, NULL);for (int i = 0; i < 8; i++) {create(Tproduce);create(Tconsume);}
}

问题1:不管该线程是否会执行,线程都会将锁拿到自己手上(互斥)

在sum.c的程序中,join函数就是一种互斥锁

join函数(等待所有线程结束)

#include "thread.h"#define N 100000000long sum = 0;void Tsum() {for (int i = 0; i < N; i++) {sum++;}
}int main() {create(Tsum);create(Tsum);join();printf("sum = %ld\n", sum);
}

任何同步问题都会有先来先等待的问题

2.条件变量API

将pc.c的自旋变成睡眠。

wait(cv, mutex) 
调用时必须保证已经获得 mutex
释放 mutex、进入睡眠状态
signal/notify(cv) 💬 私信:走起
如果有线程正在等待 cv,则唤醒其中一个线程
broadcast/notifyAll(cv) 📣 所有人:走起
唤醒全部正在等待 cv 的线程

错误代码:

#include "thread.h"
#include "thread-sync.h"int n, count = 0;
mutex_t lk = MUTEX_INIT();
cond_t cv = COND_INIT();void Tproduce() {while (1) {mutex_lock(&lk);if (count == n) {cond_wait(&cv, &lk);}printf("("); count++;cond_signal(&cv);mutex_unlock(&lk);}
}void Tconsume() {while (1) {mutex_lock(&lk);if (count == 0) {pthread_cond_wait(&cv, &lk);}printf(")"); count--;cond_signal(&cv);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);}
}

代码在只有两个线程时工作的很好,在有多个线程的时候出错,为什么呢?

两个cons睡眠,一个prod输出后唤醒一个cons。cons工作完毕后唤醒另一个cons,出现错误!不可以出现同类唤醒,需要两个条件变量。

万能的并发编程方法:

        只要条件不成立,就循环睡眠

        只要任何人可以使条件成立,就广播启动所有

//需要等待条件满足时
mutex_lock(&mutex);
while (!cond) {wait(&cv, &mutex);
}
assert(cond);
// ...
// 互斥锁保证了在此期间条件 cond 总是成立
// ...
mutex_unlock(&mutex);//其他线程条件可能被满足时broadcast(&cv);

将串行程序更改为并行程序

struct job {void (*run)(void *arg);void *arg;
}while (1) {struct job *job;mutex_lock(&mutex);while (! (job = get_job()) ) {wait(&cv, &mutex);}mutex_unlock(&mutex);job->run(job->arg); // 不需要持有锁// 可以生成新的 job// 注意回收分配的资源
}

3.信号量

class Semaphore:token, waits = 1, ''def P(self, tid):                                //获取钥匙if self.token > 0:self.token -= 1                        //若有钥匙,我将得到一把钥匙,钥匙-1return Trueelse:self.waits = self.waits + tid        //否则等待唤醒return Falsedef V(self):                                    //变出一个钥匙来if self.waits:                              //如果有有人在等这个钥匙,我就不还钥匙  self.waits = self.waits[1:]            //我就唤醒一个等待状态的进程else:self.token += 1                        //没有人在等待的话,我就将钥匙的数量+1@threaddef t1(self):self.P('1')while '1' in self.waits: passcs = Truedel csself.V()@threaddef t2(self):self.P('2')while '2' in self.waits: passcs = Truedel csself.V()@markerdef mark_t1(self, state):if localvar(state, 't1', 'cs'): return 'blue'@markerdef mark_t2(self, state):if localvar(state, 't2', 'cs'): return 'green'@markerdef mark_both(self, state):if localvar(state, 't1', 'cs') and localvar(state, 't2', 'cs'):return 'red'

示例代码实现生产者消费者问题:

void producer() {P(&empty);   // P()返回 -> 得到手环printf("("); // 假设线程安全V(&fill);
}
void consumer() {P(&fill);printf(")");V(&empty);
}

在“一单位资源”明确的情况下,使用信号量是一个明智的选择

4.哲学家吃饭问题

哲学家(线程)有时候思考,有时候吃饭

吃饭的时候需要同时得到左手和右手的叉子

当叉子被其他人占有时,必须等待,如何完成同步?

如何用互斥锁/信号量实现?

失败的尝试:

#include "thread.h"
#include "thread-sync.h"#define N 3
sem_t locks[N];void Tphilosopher(int id) {int lhs = (id - 1) % N;int rhs = id % N;while (1) {P(&locks[lhs]);printf("T%d Got %d\n", id, lhs + 1);P(&locks[rhs]);printf("T%d Got %d\n", id, rhs + 1);V(&locks[lhs]);V(&locks[rhs]);}
}int main(int argc, char *argv[]) {for (int i = 0; i < N; i++) {SEM_INIT(&locks[i], 1);}for (int i = 0; i < N; i++) {create(Tphilosopher);}
}

问题在于:如果所有的人同时举起一把叉子,那就所有人都无法吃饭(死锁)

成功的办法:

不要试图使用聪明的办法,尽量使用万能的办法(互斥锁和条件变量)

mutex_lock(&mutex);
while (!(avail[lhs] && avail[rhs])) {wait(&cv, &mutex);
}
avail[lhs] = avail[rhs] = false;
mutex_unlock(&mutex);mutex_lock(&mutex);
avail[lhs] = avail[rhs] = true;
broadcast(&cv);
mutex_unlock(&mutex);

忘掉信号量,使用一个人集中管理叉子吧:分布式系统常见的解决方式(leader/follower管理)

void Tphilosopher(int id) {send_request(id, EAT);P(allowed[id]); // waiter 会把叉子递给哲学家philosopher_eat();send_request(id, DONE);
}void Twaiter() {while (1) {(id, status) = receive_request();if (status == EAT) { ... }if (status == DONE) { ... }}
}

但是twaiter可能会忙不过来(1000w并发怎么办)

ok课程结束放个炸鸡

http://www.dtcms.com/a/570361.html

相关文章:

  • 外贸网站优化免费渠道北京网页设计公司有哪些
  • 怎样凡科建设网站定制手机号码官方网站
  • 广西柳州网站建设郑州展厅设计制作公司
  • 电子商务网站建设经费网站的方案
  • 网站被攻击了怎么办?如何进行DDoS防御?
  • 肯达建设网站用什么做网站最好
  • 网站建设交流论坛地址茂名网站制作推广
  • 个人网站的设计与开发免费静态网页
  • 链表节点复用
  • 【经典书籍】《人月神话》第十三章“金牌团队与银牌团队”精华讲解
  • ThreadPoolExecutor 的七个参数
  • 为进一步加强校园网站建设wordpress 页面 固定链接
  • C 命令行参数
  • 做网站都需要什么工具网站备案 营业执照副本
  • springboot测试临时数据修改指南
  • 从零到一:Kubernetes 基础概念与集群部署详解
  • 如何优化SQL查询性能?测试与开发协同优化指南
  • 衡水安徽网站建设网站建设需要注意什么 知乎
  • 手机怎么制作网站教程视频犀牛云做网站怎么样
  • 苏州建设网站wordpress页面编辑插件
  • 学习Java第五十九天——MySQL篇
  • 公司网站搭建费用用上网做任务的网站
  • 甘肃省城乡与建设厅网站首页关键词是在网站后台做的吗
  • 营销印刷网站wordpress模板网站标题重复
  • 室内空间设计网站推荐开网店需要什么准备
  • 烟台城乡建设学校网站开发公司支付前期物业开办费包括哪些内容
  • 【仓颉纪元】仓颉三方库适配深度实战:7 天打通 SQLite 生态壁垒
  • 电源完整性07-如何确定PDN网络中的大电容
  • Java - 使用虹软人脸识别sdk记录
  • 江门网页建站模板慧算账代理记账公司