1、整型信号量
信号量定义为一个整型量S,S≤0 表示该资源已被占用;S≥0 表示资源可用
进程企图进入临界区时,S≤0 禁止进入,S≥0 可以进入
- wait 操作 ---- P操作
- signal操作 ---- V操作
存在问题:S<=0时,会让进程(线程)处于“忙等”状态
2、记录型信号量semaphore
typedef struct {
int value;
list<PROC> L; //阻塞进程队列
}semaphore;
- value>0 时,value为资源可用数目
- value<0 时,|value|为已阻塞进程的数目
- L为阻塞进程队列首指针
3、AND信号量
申请n种资源,每种1个,或者全部分配,或者不分配,避免死锁
4、信号量集
申请n种资源,资源S_i(1 <= i <= n)申请d_i个资源且资源个数下限为t_i,或者全部分配,或者不分配,避免死锁
- Swait(S_1, 1, 1, …, S_n, 1, 1),即所有t_i、d_i都等于1,等价于AND信号量的Swait(S_1, …, S_n)
- Swait(S, 1, 1):退化为记录型信号量,但执行效率低
- Swait(S, 1, 0):特殊信号量,S.value>=1 时允许多进程进入临界区;S<=0 时阻止所有进程进入临界区,相等于一个开关
策略 :
- 寻找临界资源
- 定义信号量
- 使用前申请
- 使用后释放
5、生产者-消费者问题
信号量:
- space,存储位置,初值N;
- prod,产品,初值0;
- buf,缓冲区,初值1
循环创建M个生产者和消费者线程
void * producer(void *p) {
while(1) {
sem_wait(&space); //申请存储位置资源
sem_wait(&buf); //获取缓冲区控制权
printf("Put a producer into Buffer[%d]!\n",in);
in = (in + 1) % N; //将产品放到队尾位置并修改队尾指针
sem_post(&prod); //释放产品资源
sem_post(&buf); //释放缓冲区资源
}
return NULL;
}
void * consumer(void *p) {
while(1) {
sem_wait(&prod); //申请产品资源
sem_wait(&buf); //获取缓冲区控制权
printf("Get a producer from Buffer[%d]!\n",out);
out = (out + 1) % N; //从队头位置取产品并修改队头指针
sem_post(&space); //释放存储位置资源
sem_post(&buf); //释放缓冲区资源
}
return NULL;
}
in(队尾位置) 和 out(队头位置)为共享变量,属于临界资源(效率低下)
优化策略
信号量:
- space:存储位置,初值N
- prod:产品,初值0
- sin:队尾,初值1
- sout:队头,初值1
允许一个消费者和一个生产者同时在缓冲区放产品和取产品
6、读者-写者问题
数据集被多个并发进程(线程)共享,一些进程(线程)只读取数据集内容(读者),而另一些进程(线程)则只修改数据集内容(写者)
“读—写”互斥,“写—写”互斥,“读—读”允许
变量:readcount,读者数,初值0(共享变量,临界资源)
信号量:
- sdata,数据集,初值1;
- srcount,读者数变量,初值1;(实现对readcount的互斥访问)
第一个读者抢占数据集,最后一个离开释放数据集
读者-写者问题(写者优先)
信号量sread:读者许可资源,初值为N(读者上限)
void * writer(void *p) {
Swait(sdata, 1, 1); //申请数据集操作
Swait(sread, N, 0); //等待所有读者离开
/*Writing dataset ...*/
Ssignal(sdata, 1); //释放数据集资源
return NULL;
}
void * reader(void *p) {
Swait(sdata, 1, 0, sread, 1, 1); //判断数据集是否被写者占用,并申请1个读者许可资源
/*Reading dataset ...*/
Ssignal(sread, 1); //释放1个读者许可资源
return NULL;
}
7、哲学家进餐问题
所有哲学家都在占据右侧叉子的情况下,被阻塞起来等待自己左侧的叉子,谁也无法继续向前执行,哲学家线程将全部处于阻塞状态,永远不会被唤醒,这种状态被称为死锁
方案1
偶数编号哲学家先拿右侧叉子,再拿左侧叉子
奇数编号哲学家先拿左侧叉子,再拿右侧叉子
方案2
4号哲学家先拿左侧0号叉子,再拿右侧4号叉子
方案3(ADD型信号量)
同时申请左侧和右侧叉子资源