线程同步与互斥
多个线程同时访问共享资源可能会冲突。比如两个线程都要把某个全局变量加1,这个操作需要三条指令完成
1 从内存读变量值到寄存器
2 寄存器的值加1
3 将寄存器的值写回内存
在“读取变量的值”和“把变量的新值保存回去“,这两步操作之间插入printf调用,它会执行write系统调用进内核,为内核调用别的线程
提供了一个很好的时机。在一个循环中重复上述操作几千次,就会观察到访问冲突的现象
举一个例子:
我们创建两个线程,各自把gcount增加5000次,正常情况下,gcount最后的值应该是10000,但事实上每次运行该程序的结果都不一样,可能是5000多,也可能是6000多
1 程序代码
运行结果
出乎意料的是,这次的运行结果是对的
将代码做了如下小小的改动
运行结果
很显然,这次得到的结果符合我们该开始分析出来的结果
2 对于多线程的程序,访问冲突的问题是很普遍的,解决的办法是引入互斥锁
获得锁的线程可以完成“读-修改-写”的操作,然后释放锁给其他线程,没有获得锁的线程只能等待而不能获得共享资源,
这样,“读-修改-写”三步操作组成一个原子操作,要么都执行,要么都不执行,不会执行到中间被打断,也不会在其他处理器上并行做这个操作。
引用互斥变量的代码
运行结果
加入互斥锁后,我们发现又可以得到正常的运行结果
如果在去掉互斥锁呢?同时增加次数
运行结果
得到的结果也是正确的
但如果直接用gCount变量不加互斥锁的情况下,结果又发生了冲突
多线程之间发生冲突可能是死锁引起的,
死锁的概念:在多任务系统下,当一个或多个进程等待系统资源,而资源又被进程本身或其它进程占用时,就形成了死锁
产生死锁的原因:(1)系统资源不足
(2)进程运行推进的顺序不合适
(3)资源分配不当等
产生死锁的必要条件
(1) 互斥条件:一个资源每次只能被一个进程使用。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。