缘起
- OS自考真题中10分大题会有这道,我目前做不出来,视频链接
- 并发的一些代码题,我写不出来,现在看了一些API后,发现作者的思路,我没有读懂,根因应该是我不知道怎么去设计它,只有自己知道怎么做了,后面才是API的事
- 看了b站的HIT的视频,2倍速听这块的时候就不大灵光,但日常面试又不会单独拿出来讲,都是生产者-消费者模型,读者-写者问题,而他们的基础是这个
- 于是我又回头看下我那本薄的OS教材,以及B站上搜搜,形成了这个点
内容
前置概念
- 临界区
- 临界资源:系统中的资源一次只允许一个进程使用
- 临界区:进程中访问临界资源的程序 lionel,我之前就没有真明白这个概念,只是觉得明白了,【就是一块代码段】
- 信号量
- HIT的b站视频里,提到了原来信号表示互斥关系时,表达不过来,就引入了信号量,lionel,我理解就是bool类型表达不了,就要用int或结构体了
- 信号量只能用于P和V操作
- P是wait(),信号量小于0,进程状态置为等待状态【P是减,表示进程分配到一个资源了】
- V是signal(),信号量小于等于0,释放等待队列中一个进程,将其置为就绪态 【V是加,表示进程释放了一个资源】
互斥(mutex)
- 概念:
- 互斥好理解,排它性使用,比如多进程竞争使用打印机,使用时就得锁一下,不让别人占用
- PV操作
初始化信号量 S = 1;
进程A:
{
P(S);
临界区资源
V(S);
}
进程B:
{
P(S);
临界区资源
V(S)
}
同步
- 概念
- 两个或多个进程共同完成一项工作,比如(生产打包)过程,A进程生产,B进程打包,只有等A生产出来了,B才能打包。
- 人家描述是:让并发的进程,按我们想要的方式进行有序推进
- PV操作
- 简记就是前V后P,在后操作【打包】之前执行P(S),在前操作【生产】之后执行V(S)
初始化信号量S=0;
生产进程A:
{
生产;
V(S);
}
打包进程B:
{
P(S);
打包;
}
简单生产者-消费者问题
生产者P进程:
{
while(true) {
P(empty);
生产;
放入缓冲区;
V(full);
}
}
消费者Q进程:
{
while(true) {
P(full);
取缓冲区;
V(empty);
消费;
}
}
- code(没那么多约束场景,更像同步中的单信号的例子)
void Producer() {
while (true) {
std::this_thread::sleep_for(std::chrono::milliseconds(5));
std::unique_lock<std::mutex> lock(global_mutex);
global_deque.push_front(1);
std::cout << "生产者生产了数据" << std::endl;
global_ConditionVar.notify_all();
}
}
void Consumer() {
while (true) {
std::unique_lock<std::mutex> lock(global_mutex);
global_ConditionVar.wait(lock, [] {return !global_deque.empty(); });
global_deque.pop_back();
std::cout << "消费者消费了数据" << std::endl;
global_ConditionVar.notify_all();
}
}
小结
- PV操作必须成对出现,互斥时,它们处于同一进程,同步时,它们不在同一进程中出现。
- 同步P操作与互斥P操作在一起时,同步P操作应出现在互斥P操作前
- 互斥(S=1),同步(S=0)没太明白原因是啥,lionel
最后
进阶想懂一下
- 多个生产者-消费者问题、读者-写者问题、哲学家进餐问题 学习链接-【互斥同步经典PV操作】
- 《操作系统导论》chap31信号量,
sem_wait()就是P,sem_post()就是V,sem_t,之前也看过,没完全看懂
- 《Linux多线程服务端编程》chap02线程同步精要,提到不要用信号量,这章也是没完全看懂
- C++11 实现生产者消费者模式
过程
- 20230402先commit一版出来,后面再迭代,也是花了40min
- 问题1:其实我没太想清楚,谁跟谁是同步或互斥关系,比如简单生产者-消费者问题里,到底只是同步关系,还是说又存在了互斥关系?书上也没说清楚
- 解答:其实都是同步信号量,互斥的话要单独定义一个
mutex,但至少书中的流程没提及