线程的死锁及算法避免

线程下的死锁

死锁是什么?
是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

举个例子:你有一千万现金,现在要分别放在两个保险柜里,你把第一个保险柜的钥匙放在了第二个保险柜里了,然后不小心把第二个保险柜锁住了。接着又把第二把保险柜的钥匙放进第一个里面,然后又不小心把第一个保险柜锁住了。这个时候你的钱在保险柜里取不出来,想打卡第一个保险柜就必须先打开第二个,打开第一个就先要打开第一个,这种情况就相当于是死锁。


产生死锁的四个必要条件
(1) 互斥条件:一个资源每次只能被一个进程(线程)使用。
(2) 请求与保持条件:一个进程(线程)因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件 : 此进程(线程)已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件 : 多个进程(线程)之间形成一种头尾相接的循环等待资源关系。
#include <unistd.h> 
#include <pthread.h>
#include <string.h>

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex4 = PTHREAD_MUTEX_INITIALIZER;

static int sequence1 = 0;
static int sequence2 = 0;

int func1()
{
pthread_mutex_lock(&mutex1);
++sequence1;
sleep(1);
pthread_mutex_lock(&mutex2);
++sequence2;
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1);

return sequence1;
}

int func2()
{
pthread_mutex_lock(&mutex2);
++sequence2;
sleep(1);
pthread_mutex_lock(&mutex1);
++sequence1;
pthread_mutex_unlock(&mutex1);
pthread_mutex_unlock(&mutex2);

return sequence2;
}

void* thread1(void* arg)
{
while (1)
{
int iRetValue = func1();

if (iRetValue == 100000)
{
pthread_exit(NULL);
}
}
}

void* thread2(void* arg)
{
while (1)
{
int iRetValue = func2();

if (iRetValue == 100000)
{
pthread_exit(NULL);
}
}
}

void* thread3(void* arg)
{
while (1)
{
sleep(1);
char szBuf[128];
memset(szBuf, 0, sizeof(szBuf));
strcpy(szBuf, "thread3");
}
}

void* thread4(void* arg)
{
while (1)
{
sleep(1);
char szBuf[128];
memset(szBuf, 0, sizeof(szBuf));
strcpy(szBuf, "thread3");
}
}

int main()
{
pthread_t tid[4];
if (pthread_create(&tid[0], NULL, &thread1, NULL) != 0)
{
_exit(1);
}
if (pthread_create(&tid[1], NULL, &thread2, NULL) != 0)
{
_exit(1);
}
if (pthread_create(&tid[2], NULL, &thread3, NULL) != 0)
{
_exit(1);
}
if (pthread_create(&tid[3], NULL, &thread4, NULL) != 0)
{
_exit(1);
}

sleep(5);
//pthread_cancel(tid[0]);

pthread_join(tid[0], NULL);
pthread_join(tid[1], NULL);
pthread_join(tid[2], NULL);
pthread_join(tid[3], NULL);

pthread_mutex_destroy(&mutex1);
pthread_mutex_destroy(&mutex2);
pthread_mutex_destroy(&mutex3);
pthread_mutex_destroy(&mutex4);

return 0;
}

下列方法有助于最大限度地降低死锁:

按同一顺序访问对象。 
如果所有并发事务按同一顺序访问对象,则发生死锁的可能性会降低。例如,如果两个并发事务获得 Supplier 表上的锁,然后获得 Part 表上的锁,则在其中一个事务完成之前,另一个事务被阻塞在 Supplier 表上。第一个事务提交或回滚后,第二个事务继续进行。不发生死锁。将存储过程用于所有的数据修改可以标准化访问对象的顺序

避免事务中的用户交互。 
避免编写包含用户交互的事务,因为运行没有用户交互的批处理的速度要远远快于用户手动响应查询的速度,例如答复应用程序请求参数的提示。例如,如果事务正在等待用户输入,而用户去吃午餐了或者甚至回家过周末了,则用户将此事务挂起使之不能完成。这样将降低系统的吞吐量,因为事务持有的任何锁只有在事务提交或回滚时才会释放。即使不出现死锁的情况,访问同一资源的其它事务也会被阻塞,等待该事务完成。

保持事务简短并在一个批处理中。 
在同一数据库中并发执行多个需要长时间运行的事务时通常发生死锁。事务运行时间越长,其持有排它锁或更新锁的时间也就越长,从而堵塞了其它活动并可能导致死锁。

保持事务在一个批处理中,可以最小化事务的网络通信往返量,减少完成事务可能的延迟并释放锁

使用低隔离级别。 
确定事务是否能在更低的隔离级别上运行。执行提交读允许事务读取另一个事务已读取(未修改)的数据,而不必等待第一个事务完成。使用较低的隔离级别(例如提交读)而不使用较高的隔离级别(例如可串行读)可以缩短持有共享锁的时间,从而降低了锁定争夺

使用绑定连接。 
使用绑定连接使同一应用程序所打开的两个或多个连接可以相互合作。次级连接所获得的任何锁可以象由主连接获得的锁那样持有,反之亦然,因此不会相互阻塞。

避免死锁有一般的银行家算法,该算法假设每个客户有可能在没有得到最大资源的情况下也能工作。银行家算法对每一个请求进行检查,如果它会导致不安全状态,则不满足该请求,否则便满足。检查状态是否安全是看是否有足够的资源满足距最大需求最近的客户。

对于每个线程都需要得到最大资源才能工作的情况下,避免死锁有如下的一般编程实践:

(1)请勿尝试在可能会对性能造成不良影响的长时间操作(如 I/O)中持有锁;
(2)请勿在可能直接或间接递归调用自己的函数里持有锁;
(3)一般情况下,请先使用粗粒度锁定方法,确定瓶颈,并在必要时添加细粒度锁定来缓解瓶颈。大多数锁定都是短期持有,而且很少出现争用。因此,请仅修复测得争用的那些锁定;
(4)使用多个锁定时,通过确保所有线程都按相同的顺序获取锁定来避免死锁;
(5)在等待某个资源时,使用超时机制;
(6)在系统中使用一个定时检查死锁环的机制,如果发现死锁就让一个线程的资源释放掉(数据库好像就是用这种方式)。
常见的避免死锁的算法“银行家算法”;

银行家算法中的数据结构

(1) 可利用资源向量Available。这是一个含有m个元素的数组,其中的,每一个元素代表一类可利用的资源数目,其初始值是系统中所配置的该类全部可用资源的数目,其数值随该类资源的分配和回收而动态地改变。如果Available[j]=K,则表示系统中现有Rj类资源K个。
(2) 最大需求矩阵Max。这是一个n*m的矩阵,它定义了系统中n个进程中的每一个进程对m类资源的最大需求。如果Max[i,j]=K,则表示进程i需要Rj类资源的最大数目为K。
(3) 分配矩阵Allocation。这也是一个n*m的矩阵,它定义了系统中每一类资源当前已分配给每一进程的资源数。如果Allocation[i,j]=K,则表示进程i当前已分得Rj类资源的数目为K。
(4) 需求矩阵Need。这也是一个n*m的矩阵,用以表示每一个进程尚需的各类资源数。如果Need[i,j]=K,则表示进程i还需要Rj类资源K个,方能完成其任务。
//利用银行家算法避免死锁

#include "iostream.h"
#include "iomanip.h"

#define max_source 20
#define max_process 100

int available[max_source];
int max[ max_process ][ max_source ];
int allocation[ max_process ][max_source ];
int need[ max_process ][ max_source ];
int request[ max_source ];
int requestPID;
int source; //当前系统所具有的资源种类
int process; //当前系统的进程数


//返回值
//1:表示请求的资源>还分配的资源
//2:表示请求的资源>系统剩余的资源
//3:表示不存在安全序列
//0:成功
int banker( int securitySequence[] )
{
int i , j , k;

for( i = 0 ; i < source ; i++ )
{
if( request[i] > need[requestPID][i] )
return 1;
}

for( i = 0 ; i < source ; i++ )
{
if( request[i] > available[i] )
return 2;
}

for( i = 0 ; i < source ; i++ )
{
available[i] -= request[i];
allocation[requestPID][i] += request[i];
need[requestPID][i] -= request[i];
}

int finish[ max_process ] = { 0 };
int work[ max_source ];
int currSelect;
for( i = 0 ; i < source ; i++ )
work[i] = available[i];
for( k = 0 ; k < process ; k++ )
{
bool flag = false;
输出过程
//cout<<"work"<<endl;
//for( i = 0 ; i < source ; i++ )
// cout<<setw(5)<<work[i];
//
//cout<<endl<<"need"<<endl;
for( i = 0 ; i < process && !flag ; i++ )
{
if( finish[i] == 0 )
{
flag = true;
for( j = 0 ; j < source && flag; j++ )
{
//
//cout<<setw(5)<<need[i][j];
if( need[i][j] > work[j] )
flag = false;
}
//
//cout<<endl;
}
currSelect = i;
}
if( !flag )
{
for( j = 0 ; j < source ; j++ )
{
available[j] += request[j];
allocation[requestPID][j] -= request[j];
need[requestPID][j] += request[j];
}
return 3;
}
finish[currSelect] = k+1;
//
//cout<<"allocation"<<endl;
for( j = 0 ; j < source ; j++ )
{
work[j] += allocation[currSelect][j];
//
//cout<<setw(5)<<allocation[currSelect][j];
}
//
//cout<<endl<<"当前选择"<<currSelect<<endl;
//cout<<"----------------------------------------"<<endl;
}
for( i = 0 ; i < process ; i++ )
{
j = 0;
while( finish[j] != i + 1 )
j++;
securitySequence[i] = j;
}

return 0;
}

//setCurrentState
void setCurrentState()
{
source = 3;
process = 5;
int a[] = { 3 , 3 , 2 };
int m[][ max_source ] = {
{ 7 , 5 , 3 } ,
{ 3 , 2 , 2 } ,
{ 9 , 0 , 2 } ,
{ 2 , 2 , 2 } ,
{ 4 , 3 , 3 } ,
};
int al[][ max_source ] = {
{ 0 , 1 , 0 } ,
{ 2 , 0 , 0 } ,
{ 3 , 0 , 2 } ,
{ 2 , 1 , 1 } ,
{ 0 , 0 , 2 }
};
int re[] = { 1 , 0 , 2 };
int i , j;

cout<<"当前可用资源数:"<<endl;
for( i = 0 ; i < source ; i++ )
{
available[i] = a[i];
cout<<setw(5)<<available[i];
}
cout<<endl<<endl;

cout<<"当前进程所需资源的最大数:"<<endl;
for( i = 0 ; i < process ; i++ )
{
for( j = 0 ; j < source ; j++ )
{
max[i][j] = m[i][j];
cout<<setw(5)<<max[i][j];
}
cout<<endl;
}
cout<<endl;

cout<<"当前进程已分配到资源数:"<<endl;
for( i = 0 ; i < process ; i++ )
{
for( j = 0 ; j < source ; j++ )
{
allocation[i][j] = al[i][j];
cout<<setw(5)<<allocation[i][j];
}
cout<<endl;
}
cout<<endl;

cout<<"当前进程还需分配的资源数:"<<endl;
for( i = 0 ; i < process ; i++ )
{
for( j = 0 ; j < source ; j++ )
{
need[i][j] = max[i][j] - allocation[i][j];
cout<<setw(5)<<need[i][j];
}
cout<<endl;
}
cout<<endl;

requestPID = 1;
cout<<"进程 "<<requestPID<<" 请求的资源向量:"<<endl;
for( i = 0 ; i < source ; i++ )
{
request[i] = re[i];
cout<<setw(5)<<request[i];
}
cout<<endl<<endl;
}

void main()
{
//为进行一次模拟实验
//设置系统的当前状态
setCurrentState();

int securitySequence[ max_process ] = { 0 };

cout<<"调用银行家算法..."<<endl;
int error = banker( securitySequence );
if( !error )
{
cout<<"可以响应该请求,响应后存在安全序列:"<<endl;
for( int i = 0 ; i < process ; i++ )
cout<<setw(5)<<securitySequence[i];
cout<<endl;
}
else
{
cout<<"无法响应该请求."<<endl;
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值