文档章节

线程死锁一引起的系列思考

梦想游戏人
 梦想游戏人
发布于 2016/04/07 11:11
字数 943
阅读 69
收藏 4

发生死锁必须具备以下的4个条件

 

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

:资源是互斥的

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

:如果只请求一个资源 那么也不会发现

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

:所有权

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

:比如操作系统课老师举得例子 几个人吃饭,拿筷子问题 ,最后都会没拿到2只筷子而都吃不了饭

 

死锁例子

 static  std::recursive_mutex _mutex1;
static  std::recursive_mutex _mutex2;
int hp;

 int main(int argc, char *argv[])
{
  thread t ( []()
  {  
    _mutex1.lock();
    Sleep(12);
    _mutex2.lock();
   cout << "t:"<< hp << endl;
 
    _mutex1.unlock();
   _mutex2.unlock();
  });
  t.detach();
 
  _mutex2.lock();
  Sleep(12);
  _mutex1.lock();
  cout << "main:" << hp << endl;
 

  _mutex1.unlock();
  _mutex2.unlock();
 system("pause");
 return 0;
}

mutex1 mutex2 为2个资源,造成了环路等待条件 导致线程死锁

 

递归问题:递归加锁了 如

void doWith()
{
 _mutex1.lock();
 cout << "doWith:" << hp << endl;
  _mutex1.unlock();

} 
  int main(int argc, char *argv[])
 {
 
  thread t ( [=]()
  {  
    _mutex1.lock();
 
   cout << "t:"<< hp << endl;
   doWith();
     _mutex1.unlock();
 
  });
  t.detach();
  system("pause");
 return 0;
}

mutex被递归加锁了   而 std::mutex 非递归锁,子程序和 匿名函数  位于同一个线程空间     就又会造成 死锁,因为子程序在等待 匿名函数的解锁,而进入死锁

这时候可用 std::recursive_mutex  递归锁 替代std::mutex  或者自己实现一个递归锁

原理大致是 如果 和上次成功lock的操作的线程 不在同一线程 就真正的 lock 否则仅仅是引用计数加1

 以下是我的一个实现

class Re_mutex
{
public:
 void lock()
 {
  cout << "lock id "<<this_thread::get_id() << endl;
  if (this_thread::get_id() == _last_lock_id &&_islock == true)
  {
   ++count;
  }
  else
  {
   _last_lock_id = this_thread::get_id();
   _islock = true;
   _mutex.lock();
  }
 }

 void unlock()
 {
  --count;
  if (count == 0 && _islock)
  {
   _mutex.unlock();
  }
 }
private:
 bool _islock = false;
 mutex _mutex;
 int count = 0;
 thread::id _last_lock_id;
};
static  Re_mutex _mutex1;
//static  std::recursive_mutex _mutex2;
int hp;
void doWith()
{
 _mutex1.lock();
 cout << "doWith:" << hp << endl;
 _mutex1.unlock();

}

 int main(int argc, char *argv[])
 {
 
  thread t ( [=]()
  {  
    _mutex1.lock();
 
   cout << "t:"<< hp << endl;
   doWith();
    _mutex1.unlock();
 
  });
  t.detach();
 
 
 system("pause");
 return 0;
}

解决死锁的代价比较大,所以一般解决办法是 通过良好的设计 避免 死锁

 

标准库提供了一套 避免死锁的方案

std::lock  该锁 把需要访问的资源 要么都加锁 要么都不加锁 来避免死锁

template<class _Lock0,
	class _Lock1,
	class... _LockN> inline
	void lock(_Lock0& _Lk0, _Lock1& _Lk1, _LockN&... _LkN)
	{	// lock N mutexes
	int _Res = 0;
	while (_Res != -1)
		_Res = _Try_lock(_Lk0, _Lk1, _LkN...);
	}

 

lock_guard可以通过调用重载构造函数 来避免加锁 ,来RAII资源

	std::lock_guard<std::mutex>  l(m,std::adopt_lock);

 

避免死锁的一些方法

避免嵌套锁
一个线程已经获得了一个锁的同时 别去获取第二个 如果需要 那就用std::lock 来获取

避免在持有锁的情况下调用用户代码,因为你不知道永不要做什么

使用固定顺序获取锁,如获取了A才能获取B,这样就不会导致循环等待的情况, 当然std::lock的原理就是这个






 

 

© 著作权归作者所有

共有 人打赏支持
上一篇: 文件权限
下一篇: map和unordered_map
梦想游戏人
粉丝 38
博文 444
码字总数 127453
作品 0
成都
私信 提问
多线程程序与fork()

多线程程序里不准使用fork UNIX上C++程序设计守则3 准则3:多线程程序里不准使用fork 在多线程程序里,在”自身以外的线程存在的状态”下一使用fork的话,就可能引起各种各样的问题.比较典型的...

长平狐
2012/09/03
511
0
java并发编程——死锁

第一部分:概述 我们用经典的“哲学家进餐”问题来理解死锁的概念。五个哲学家坐在一个圆桌旁,他们一共只有五根筷子(不是五双),每两人中间有一根筷子,他们时而思考,时而吃饭,吃完以后...

isam
2016/05/27
71
2
教程:一起学习Hystrix--Hystrix断路器配置及使用场景

目录 Hystrix本系列博文 如何配置以及调整断路器 预料中的抖动和失败 潜在风险 声明 Hystrix本系列博文 以下为博主写Hystrix系列的文章列表如下: 点击查看 Hystrix入门 点击查看 Hystrix命令...

java_龙
2018/05/27
0
0
iOS多线程全面解读(四):锁

写在前面 本系列文章列表 概述、NSThread GCD NSOperation 锁 多线程带来的问题之一就是安全问题,“锁”是为了使多个线程间可以相互排斥地使用全局变量等共享资源,简单来说就是保证同一时刻...

indulge_in
2017/11/30
0
0
多线程程序设计学习(2)之single threaded execution pattern

Single Threaded Execution Pattern【独木桥模式】 一:single threaded execution pattern的参与者 --->SharedResource(共享资源) 二:single threaded execution pattern模式什么时候使用 ......

无信不立
2015/07/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Spark集群安装方式2

环境: jdk1.8、hadoop-2.7、spark-1.6、三台centos7(如下List-1所示) List-1 如下30上部署master;31上部署worker1;32上部署worker2 192.168.33.30 master192.168.33.31 worker1192.168.......

克虏伯
18分钟前
1
0
java File常用的方法

import java.io.File; public class a_22 { public static void main(String[] args) {File f= new File("D:\\tianya\\2019.3.14\\html");System.out.println(f.isDirectory());Syste......

南桥北木
25分钟前
1
0
equals()的重写规则

自反性。对于任何非null的引用值x,x.equals(x)应返回true。 对称性。对于任何非null的引用值x与y,当且仅当:y.equals(x)返回true时,x.equals(y)才返回true。 传递性。对于任何非null的引用...

无精疯
52分钟前
2
0
Go基础系列:双层channel用法示例

双层通道的解释见Go的双层通道 以下是一个双层通道的使用示例。注意下面的示例中使用了"信号通道"(Signal channel),但这里的信号通道是多余的,仅仅只是为了介绍。 信号通道不用来传递数据,...

echojson
今天
2
0
PHP文件上传error的错误类型

PHP文件上传error的错误类型 - $_FILES['file']['error'] 有以下几种类型 1、UPLOAD_ERR_OK 其值为 0,没有错误发生,文件上传成功。 2、UPLOAD_ERR_INI_SIZE 其值为 1,上传的文件超过了 ph......

小良下山化了个缘
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部