1.温故知新
通过对信号量的访问和修改,让进程有序推进
问题: empty值必须是正确的,如果empty错了,就不能有序推进了
2.共同修改信号量引出的问题
//生产者
Producer(item)
{
P(empty);//生产者先判断 缓存区个数 empty是否满了,empty == 0,阻塞
...
}
//生产者P1
register = empty;
register = register - 1;
empty = register;
//生产者P2
register = empty;
register = register - 1;
empty = register;
//初始情况
empty = -1; //空闲缓冲区的个数,-1表示有一个进程在睡眠
//一个可能的执行(调度)
P1.register = empty; // P1.register = -1
P1.register = P1.register - 1; // P1.register = -2
P2.register = empty; // P2.register = -1;
P2.register = P2.register - 1; // P2.register = -2
empty = P1.register; // empty = -2
empty = P2.register; // empty = -2
如果正确执行,empty初始值为 -1,P1执行完,empty = -2,P2执行完,empty = -3
上边的例子,empty = -2
所以,信号量empty 需要保护
3.竞争条件
竞争条件:和调度有关的共享数据语义错误
错误是由多个进程并发操作共享数据 引起的
错误和调度顺序有关,很难发现和调试
需要加保护,保证调度的正确执行
有的程序中 会加空循环,减少调度的错误概率,但是不会根本解决调度问题
4.解决竞争条件的直观想法
在写共享变量empty时,给empty上锁, 阻止其他进程访问empty
受保护的 代码段 一次只允许一个进程进入,不能被分割的代码段称为原子操作
//仍然是上边出错的执行序列
P1.register = empty;
P1.register = P1.register - 1;
P2.register = empty;
P2.register = P2.register - 1;
empty = P1.register;