持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第7天,点击查看活动详情
并发控制原理
P-V原语
原语就是原子语义,P-V减和加两个操作具有原子性,即全部执行完成或都不执行,并且在执行过程中只能有一个线程进入执行,那为什么需要原子语义的操作呢?这涉及到并发与并行操作,就是上述一个变量加1不做原子性操作可能发生的情况。
P-V原语是P和V操作具有线程安全性,即原子性,可以查看以下伪代码:
int num = 0;
add(){
num = num +1;
}
subtract(){
num= num - 1;
}
简单而言就是对一个变量进行增加或减少,以保证线程安全性的操作就称为P-V原语。
信号量
信号量是依赖于P-V原语来构建的一套线程安全机制,就是给定一个初始资源数n,然后利用P-V原语对n进行操作,当n大于或等于0时,任务获得资源执行,当n小于0时,对任务进行阻塞。
具体实现伪代码如下:
int num = 0;
Queue queue;
p(){ //原子性操作
if(count <0){ //当资源数小于0时表明没有足够资源,那么去队列阻塞
queue.add(Thread);
while(count < 0) wait;
}
num = num - 1;
}
v(){ //原子性操作
num =num +1;
if(num > 0 && queue.isNotEmpty()){ //当资源数大于0并且队列不为空时,唤醒等待资源的任务
Task task= queue.remove();
Notify task;
}
}
上述代码就是信号量的核心代码,信号量就是利用P-V原语对num变量操作,然后配合一个等待队列进行任务阻塞。
互斥量
互斥量就是信号量的特殊版本,信号量有多个资源,互斥量则有且只有一个资源,就是信号量为1的特殊情况,但是,它们之间也有所不同。
代码如下:
ConditionVariables conditionVariables;
Thread a(){
while(!conditionVariables){
wait();
}
}
Thread b(){
while(!conditionVariables){
wait();
}
}
初始化一个条件变量,当条件不满足时,所有任务都阻塞等待,直到条件变量满足条件后,唤醒所有等待任务。注意点是这里唤醒是所有任务,而不是唤醒一个,这也是与信号量不同的点,信号量是用P-V原语对一个num增加或减少来唤醒任务或者阻塞任务。