线程同步与互斥

多个线程同时访问共享资源可能会冲突。比如两个线程都要把某个全局变量加1,这个操作需要三条指令完成

1 从内存读变量值到寄存器

2 寄存器的值加1

3 将寄存器的值写回内存

在“读取变量的值”和“把变量的新值保存回去“,这两步操作之间插入printf调用,它会执行write系统调用进内核,为内核调用别的线程

提供了一个很好的时机。在一个循环中重复上述操作几千次,就会观察到访问冲突的现象

举一个例子:

我们创建两个线程,各自把gcount增加5000次,正常情况下,gcount最后的值应该是10000,但事实上每次运行该程序的结果都不一样,可能是5000多,也可能是6000多

1    程序代码

线程同步与互斥线程同步与互斥

运行结果

线程同步与互斥

出乎意料的是,这次的运行结果是对的

将代码做了如下小小的改动

线程同步与互斥

运行结果

线程同步与互斥

很显然,这次得到的结果符合我们该开始分析出来的结果

2 对于多线程的程序,访问冲突的问题是很普遍的,解决的办法是引入互斥锁

获得锁的线程可以完成“读-修改-写”的操作,然后释放锁给其他线程,没有获得锁的线程只能等待而不能获得共享资源,

这样,“读-修改-写”三步操作组成一个原子操作,要么都执行,要么都不执行,不会执行到中间被打断,也不会在其他处理器上并行做这个操作。

引用互斥变量的代码

线程同步与互斥

运行结果

线程同步与互斥

加入互斥锁后,我们发现又可以得到正常的运行结果

如果在去掉互斥锁呢?同时增加次数

线程同步与互斥

运行结果

线程同步与互斥

得到的结果也是正确的

但如果直接用gCount变量不加互斥锁的情况下,结果又发生了冲突

线程同步与互斥

线程同步与互斥

多线程之间发生冲突可能是死锁引起的,

死锁的概念:在多任务系统下,当一个或多个进程等待系统资源,而资源又被进程本身或其它进程占用时,就形成了死锁

产生死锁的原因:(1)系统资源不足

                             (2)进程运行推进的顺序不合适

                             (3)资源分配不当等

产生死锁的必要条件

 (1) 互斥条件:一个资源每次只能被一个进程使用。

(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。 
避免死锁:
(1)按统一顺序访问对象
(2)避免事务中的用户交互
(3)保持事务简短并处于一个批处理中
(4)使用较低的隔离级别
(5)使用绑定连接