线程同步与互斥及死锁问题

来源:互联网 发布:腾讯离职 知乎 编辑:程序博客网 时间:2024/06/01 21:03

线程我们之前已经总结过了,就是进程的一个执行分支。既然是一个执行分支,那么我们就可以定义两个线程,在定义一个全局变量,初始值置为0,让这两个线程,同时对这个全局变量进行++处理,1000次,100000次,100000000次,之后,你会发现,数值越大,你得到想要的结果的几率就越小。这是为什么呢?

这个全局变量可以被这两个线程同时看见,那么他就是临界资源了,但是线程对临界资源访问,却不原子性的,那么出错就难免的了

还有就是线程间的切换,也可能导致错误。

那么怎么解决呢?

既然它们的访问不是原子性的,那么保证原子性就行了。还记得互斥吗?互斥说的是:在任意时刻,只有一个执行流访问临界区。

我们对临界资源上把锁——互斥锁,来解决这个问题:

互斥锁的创建和销毁

 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int pthread_mutex_destroy(pthread_mutex_t *mutex);

加锁和解锁:

int pthread_mutex_lock(pthread_mutex_t *mutex)//加锁int pthread_mutex_trylock(pthread_mutex_t *mutex)//加锁int pthread_mutex_unlock(pthread_mutex_t *mutex)//解锁

这里有两种加锁的函数,那么他们有什么区别呢?第一个函数,去申请资源,如果没有资源,那么会一直等,知道有资源;而第二个函数,如果没有申请到资源,那么会隔段时间问问,有没有资源,可以说成:轮询

加锁之后,那么线程就变成串行的了,虽然保证了原子性,不过线程的优点就发挥不出来了。

互斥锁和二元信号量在理论上是差不多的。不过,一个用于线程,一个用于进程

互斥锁可以被多个线程同时申请,那么互斥锁同样也是临界资源,既然是临界资源,那么如果两个或多个线程同时争夺这个锁 ,怎么办,这就是死锁。

死锁的概念: 是指两个或两个以上的进程(线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外部处理作用,它们都将无限等待下去。

死锁形成原因:

1、系统资源不足;

2、进程(线程)推进的顺序不恰当;

3、资源分配不当。

死锁的形成场景:

1、忘记释放锁:在申请锁和释放锁之间直接return

2、单线程重复申请锁:一个线程,刚出临界区,又去申请资源。

3、多线程多锁申请:两个线程,两个锁,他们都已经申请了一个锁了,都想申请对方的锁

4、环形锁的申请:多个线程申请锁的顺序形成相互依赖的环形

死锁产生的必要条件 :

1、互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用完释放。

2、请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。

3、不可抢占条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。

4、环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。

处理死锁的方法:

1、预防死锁:可以破坏产生死锁的必要条件:破坏“请求和保持”条件;破坏“不可抢占”条件;破坏“循环等待”条件

2、避免死锁:银行家算法

算法原理:把操作系统看作是银行家,操作系统管理的资源相当于银行家管理的资金,进程向操作系统请求分配资源相当于用户向银行家贷款。为保证资金的安全,银行家规定:

(1) 当一个顾客对资金的最大需求量不超过银行家现有的资金时就可接纳该顾客;

(2) 顾客可以分期贷款,但贷款的总数不能超过最大需求量;

(3) 当银行家现有的资金不能满足顾客尚需的贷款数额时,对顾客的贷款可推迟支付,但总能使顾客在有限的时间里得到贷款;

(4) 当顾客得到所需的全部资金后,一定能在有限的时间里归还所有的资金.

算法:

设进程cusneed提出请求REQUEST [i],则银行家算法按如下规则进行判断。
(1)如果REQUEST [cusneed]
(2)如果REQUEST [cusneed] [i]<= AVAILABLE[i],则转(3);否则,等待。
(3)系统试探分配资源,修改相关数据:
AVAILABLE[i]-=REQUEST[cusneed][i];
ALLOCATION[cusneed][i]+=REQUEST[cusneed][i];
NEED[cusneed][i]-=REQUEST[cusneed][i];
(4)系统执行安全性检查,如安全,则分配成立;否则试探险性分配作废,系统恢复原状,进程等待。

3、检验死锁和解除死锁:

先检测:这种方法并不须事先采取任何限制性措施,也不必检查系统是否已经进入不安全区,此方法允许系统在运行过程中发生死锁。但可通过系统所设置的检测机构,及时地检测出死锁的发生,并精确地确定与死锁有关的进程和资源。检测方法包括定时检测、效率低时检测、进程等待时检测等;

然后解除死锁:采取适当措施,从系统中将已发生的死锁清除掉。