有过多线程编程经验的朋友,很多都吃过死锁的苦,如何能避免死锁,在开发中应该注意哪些问题。

2010-12-27 23:50:12

11 Answers

要从根本上避免,就是采用简单的线程通讯模型,比如只采用消息队列方式来通讯。
在Windows平台上,采用SendMessage, PostMessage

2010-12-28 03:50:44

有一个经验:最好不要嵌套使用同步方式。

同步的操作应该是个原子操作,要么没锁,要么锁。如果处于锁之中,那么结果就无法确定,很多时候就变成了死锁。嵌套使用就很难避免这种状况出现

2010-12-28 04:43:42

1.要充分考虑多个线程之间的线程调度,如下两个线程代码:
线程1:
Acquire A
acquire B
Release A
Release B

线程2:
Acquire B
Acquire A
Release B
Release A

若线程1执行完1、2两句,线程2再调度执行,或线程2执行完1、2两句,线程1再调度执行。都不会发生死锁。
而若线程1执行完Acquire A时,切换到线程2执行Acquire B,此时将发生死锁。线程1等待B,线程2等待A,而B和A将永远无法释放。
以上两种调度情况都有可能发生,所以会产生死锁。

2.函数进入时申请了锁,函数返回前,别忘记释放掉锁。
3.若在驱动程序中使用自旋锁,必须在低于或者等于DISPATCH_LEVEL的IRQL下使用,因为线程调度是在DISPATCH_LEVEL级别完成的。否则线程得不到调度,会出现死机现显。

2010-12-28 06:09:35

无法完全避免死锁,其实更加实际的提问方式是,如何在特定平台和系统上去检测和定位死锁?
比如在android平台,经常会通过查看data\anr\traces.txt系统的日志信息来分析死锁问题。

2010-12-28 08:04:46

要避免死锁先要知道死锁的原因,在网上找到了一篇说得还行的:

操作系统中有若干进程并发执行, 它们不断申请、使用、释放系统资源,虽然系统的进
程协调、通信机构会对它们进行控制,但也可能出现若干进程都相互等待对方释放资源才能
继续运行,否则就阻塞的情况。此时,若不借助外界因素, 谁也不能释放资源, 谁也不能解
除阻塞状态。根据这样的情况,操作系统中的死锁被定义为系统中两个或者多个进程无限期
地等待永远不会发生的条件,系统处于停滞状态,这就是死锁。
产生死锁的原因主要是:
(1) 因为系统资源不足。
(2) 进程运行推进的顺序不合适。
(3) 资源分配不当等。
如果系统资源充足, 进程的资源请求都能够得到满足,死锁出现的可能性就很低, 否则
就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。
产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
这四个条件是死锁的必要条件, 只要系统发生死锁, 这些条件必然成立, 而只要上述条件之
一不满足,就不会发生死锁。

死锁的解除与预防:
理解了死锁的原因, 尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和
解除死锁。所以, 在系统设计、进程调度等方面注意如何不让这四个必要条件成立, 如何确
定资源的合理分配算法, 避免进程永久占据系统资源。此外, 也要防止进程在处于等待状态
的情况下占用资源。因此,对资源的分配要给予合理的规划。

转自:http://iask.sina.com.cn/b/1891920.html

2010-12-28 09:37:03

我有一个思路,希望可以给你启发:在可能发生死锁的位置记录时间,当超过一定时间仍无法获得全部资源,则认为发生死锁。这种情况下强制释放已经占用的资源(如果已经对资源进行了处理,记得要回滚),重新申请。
这么做的好处是可以有效解决已经发生的死锁。
缺点是只有在某些特点条件下才能使用这种方式,同时这么做对性能等也会产生影响,并增加代码的复杂度。
希望我的思路对你有帮助。

2010-12-28 11:26:51
您不能回答该问题或者回答已经关闭!

相关文章推荐

  • C#实例解析适配器设计模式

    将一个类的接口变成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够一起工作

  • C#中using指令的几种用法

    using + 命名空间名字,这样可以在程序中直接用命令空间中的类型,而不必指定类型的详细命名空间,类似于Java的import,这个功能也是最常用的,几乎每个cs的程序都会用到

  • C#协变和逆变

    “协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型,“逆变”则是指能够使用派生程度更小的类型

  • C#运行时相互关系

    C#运行时相互关系,包括运行时类型、对象、线程栈和托管堆之间的相互关系,静态方法、实例方法和虚方法的区别等等

  • C#开发中的反射机制

    反射的定义:审查元数据并收集关于它的类型信息的能力。元数据(编译以后的最基本数据单元)就是一大堆的表,当编译程序集或者模块时,编译器会创建一个类定义表,一个字段定义表,和一个方法定义表等

  • 使用托管C++粘合C#和C++代码(二)

    本文实现一下C++代码调用C#代码的过程。我构造一个简单并且直观的例子:通过C++ UI 触发C# UI.

  • C#开发高性能Log Help类设计开发

    项目中要在操作数据库的异常处理中加入写Log日志,对于商业上有要求,写log时对其它操作尽可能影响小,不能因为加入log导致耗时太多

  • Async和Await使异步编程更简单

    C#5.0中async和await两个关键字,这两个关键字简化了异步编程,之所以简化了,还是因为编译器给我们做了更多的工作

  • C#中的索引器的简单理解和用法

    C#中的类成员可以是任意类型,包括数组和集合。当一个类包含了数组和集合成员时,索引器将大大简化对数组或集合成员的存取操作

  • C#基础概念之延迟加载

    延迟加载(lazy load)是Hibernate3关联关系对象默认的加载方式,延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作

  • 使用托管C++粘合C#和C++代码(一)

    C#在xml读写,数据库操纵,界面构造等很多方面性能卓越;C++的效率高,是底层开发的必备武器

  • 深入C# 序列化(Serialize)、反序列化(Deserialize)

    C#中的序列化和反序列化,序列化是.NET运行时环境用来支持用户定义类型的流化的机制