每天回家路上总有一段比较长的路一片漆黑无法看书。这种时候,如果我有兴致则会用手机上上网,但是大部分情况下还是用来想问题的。刚才在路上想起今天的工作之一是让一个类型中的所有方法对多线程“完全互斥”——我不知道如何为它命名,我的意思是一个类中任意两个方法A或B,在A没有退出前,另一个线程是无法访问B的(当然也无法访问A)。最简单的方式应该是把每个方法标记为:

[MethodImpl(MethodImplOptions.Synchronized)]
public void SomeMethod() { ... }

但是这意味着每进入一个方法,都会自动lock(方法所在的类型),锁定这样一个公开对象(甚至还是跨AppDomain的)自然不是一个好的做法。更好的做法是声明一个私有变量,然后对它进行lock。但是这意味着每个方法都需要用lock包含,我嫌麻烦,不知怎么又想尝试着使用一个公用的Lock方法,并传入一个Action对象,这样lock语句就只出现一次了:

private object m_mutex = new object();
private void Lock(Action action) { lock (this.m_mutex) action(); }

但是,这又意味着每个公开方法内部都要使用Lock方法,这和直接使用lock(this.m_mutex)又有什么区别呢?区别当然是有的,硬要说起来,使用Lock方法意味着“如果某一天”我要把“互斥”这个条件去掉的话,我只要修改Lock方法一个就可以了——否则我需要修改所有的公开方法。

当然,我觉得就这点理论上的“优势”是不足以修改代码的,那么我还是继续使用MethodImplOptions.Synchronized方式吧。

经过了上面这一圈没有带来多大价值的思考之后,我又回忆起今天园子首页的一篇文章谈到死锁。死锁很容易出现,例如下面的代码引发死锁的概率几乎是100%:

var mutexA = new object();
var mutexB = new object();ThreadPool.QueueUserWorkItem(_ =>
{lock (mutexA){Console.WriteLine("Mutex A acquired.");Thread.Sleep(1000);Console.WriteLine("Trying to acquire mutex B.");lock (mutexB){Console.WriteLine("Mutex B acquired.");}}
});ThreadPool.QueueUserWorkItem(_ =>
{lock (mutexB){Console.WriteLine("Mutex B acquired.");Thread.Sleep(1000);Console.WriteLine("Trying to acquire mutex A.");lock (mutexA){Console.WriteLine("Mutex A acquired.");}}
});

这种情况下两个内层lock中的代码都无法执行,因为每个线程都在等待对方释放才能继续下去,这种mutex锁定顺序不一致的情况导致死锁。那么概括下来,什么情况下会出现死锁呢?其实就是:“如果线程A正持有对象a而请求锁定b,同时线程B持有b而请求锁定c,同时线程C持有c而请求……锁定a”,无论这个循环有多长,其中涉及到多少个线程,一旦出现这种循环,则进入死锁。其实我想任何一本讲操作系统的书都会谈到到如何检查死锁——以及解开死锁。既然lock语句只能让我们静悄悄地等待下去,那么不如由我们自己提供一个实现,避免发生死锁的情况。例如:

public static class Lock
{public static void With(object mutex, Action action) { ... }
}

于是原本使用lock的语句现在就可以变成:

//lock (mutex)
//{
//    ...
//}Lock.With(mutex, () =>
{...
});

而在Lock.With方法中,我们除了调用Monitor.Enter/Exit方法来实现真正的锁之外,还需要在Enter之前判断这个mutex能否正确获得。其实就是查看一点:于此同时是否有另一个线程正持有当前mutex对象,并且(经过一个“链”)也在等待当前线程正持有的其他mutex对象。如果出现了这样的情况,则Lock.With不会调用Monitor.Enter,而是抛出异常。这样做肯定是可行的,问题的关键在于如何设计一个方便使用,性能优越,并且线程安全的数据结构。

可惜,等我兴冲冲地回到家,打开电脑,在搜索引擎敲入“.NET Deadlock Detect”之后,却找到了MSDN Magazine上的两篇文章——原来又是别人的二手货。《Advanced Techniques To Avoid And Detect Deadlocks In .NET Apps》中讲述了检查和打破死锁的算法,而《Deadlock monitor》一文中甚至将我想要做的东西完全实现了出来。简单的说,这儿已经没我什么事情了。感兴趣的朋友们可以阅读这两篇文章,提到了实践中我考虑到和没有考虑到的各种细节。仔细研究一遍,相信会有很大帮助的。

当然,这种做法只适合在测试环境中“检查”是否有可能出现死锁情况,在实际情况下这种做法还是非常消耗性能的。不过,我们可以在编译产品环境的时候使用特别的编译选项,把用于检查死锁的代码给短路掉,这自然就没有任何问题了。

转载于:https://www.cnblogs.com/JeffreyZhao/archive/2009/09/10/dotnet-deadlock-detect.html

监视程序中的死锁及其他相关推荐

  1. Java程序中的死锁

    什么是死锁? 死锁是一种特定的程序状态,主要是由于循环依赖导致彼此一直处于等待中,而使得程序陷入僵局,相当尴尬.死锁不仅仅发生在线程之间,而对于资源独占的进程之间同样可能出现死锁.通常来说,我们所说的 ...

  2. 监视和检测Java应用程序中的内存泄漏

    因此,您的应用程序内存不足,您日夜不停地分析应用程序,以期捕获对象中的内存漏洞. 后续步骤将说明如何监视和检测您的内存泄漏,以确保您的应用程序安全. 1.怀疑内存泄漏 如果您怀疑有内存泄漏,可以使用一 ...

  3. Java 程序中的多线程

    在 Java 程序中使用多线程要比在 C 或 C++ 中容易得多,这是因为 Java 编程语言提供了语言级的支持.本文通过简单的编程示例来说明 Java 程序中的多线程是多么直观.读完本文以后,用户应 ...

  4. 操作系统(13)-操作系统中的死锁及其预防、避免、检测与解除

    1 死锁的基本概念 死锁的定义:一组进程中,每个进程都无限等待被该组进程中另一进程所占有的资源,因而永远无法得到的资源,这种现象称为进程死锁,这一组进程就称为死锁进程.如果死锁发生,会浪费大量系统资源 ...

  5. sql server死锁_如何解决SQL Server中的死锁

    sql server死锁 In this article, we will talk about the deadlocks in SQL Server, and then we will analy ...

  6. MFC应用程序中添加控制台窗口

    在MFC程序中输出调试信息的方法有两种,一种是使用TRACE宏,可以向Output窗口输出调试信息:另一种是用MessageBox,弹出消息框来输出调试信息,但会影响程序的运行. 其实有一种方法可以更 ...

  7. java产生死锁的主要原因_详解java中产生死锁的原因及如何避免

    1. Java中导致死锁的原因 Java中死锁最简单的情况是,一个线程T1持有锁L1并且申请获得锁L2,而另一个线程T2持有锁L2并且申请获得锁L1,因为默认的锁申请操作都是阻塞的,所以线程T1和T2 ...

  8. 在python程序中的进程操作

    ********在python程序中的进程操作********之前我们已经了解了很多进程相关的理论知识,了解进程是什么应该不再困难了,刚刚我们已经了解了, 运行中的程序就是一个进程.所有的进程都是通过 ...

  9. 在.NET客户端程序中使用多线程

    在.NET客户端程序中使用多线程通常认为在编写程序中用到多线程是一个高级的编程任务,容易发生错误.在本月的栏目中,我将在一个Windows窗体应用程序中使用多线程,它具有实际的意义,同时尽量使事情简单 ...

最新文章

  1. Windows下获取视频设备的一种改进实现
  2. ORA-01658: 无法为表空间space中的段创建 INITIAL
  3. matlab人工势场法三维演示图,人工势场法(Artificial Potential Field Method)的学习
  4. java中修饰常量的事_浅谈java中的声明常量为什么要用static修饰
  5. 深圳敏捷云计算机科技,敏捷云 | 关于我们 | 敏捷云
  6. 约束布局(ConstraintLayout)1.1.2 版本的新特性
  7. 二数 (埃森哲杯第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛)...
  8. [BZOJ 1025] [SCOI2009] 游戏 【DP】
  9. Visual Studio 2013 编译Notepad++源码
  10. 0505.Net基础班第十一天(面向对象继承)
  11. 即时通讯源码php开源版下载附安装教程+演示
  12. 【烈日炎炎战后端】Redis(6.1万字)
  13. LZY最喜欢的思维题
  14. Codeforces 1023G:Pisces(最长反链)
  15. 产品 电信nb接口调用_电信物联网平台NBIoT使用Postman模拟测试接口
  16. 《深入理解计算机系统》Lab3 Bomblab
  17. linux修改tomcat默认的8080端口号
  18. ip地址与MAC地址 中的 组播
  19. MongoDB数据库常用SQL命令
  20. Linux下文件系统的转换

热门文章

  1. anaconda的执行路径
  2. xfce4终端的字体颜色修改
  3. yelee主题中加入revolvermaps插件
  4. haroopad故障
  5. 最简洁的y460显卡切换安装方式
  6. 线性代数导论4——A的LU分解
  7. 斯坦福大学机器学习第三课“多变量线性回归“
  8. FusionInsight LibrA V100R002C80SPC300安装指南
  9. MS15-035 EMF文件处理漏洞分析与POC构造
  10. 如何检查CentOS服务器受到DDOS攻击