勇哥注:
《多线程安全》这个系列会持续写下去,它是我的一个弱点,有兴趣的朋友可以选择性看看。
先看源码:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { Test(); } static void LockTooMuch(object lock1, object lock2) { lock (lock1) //锁定lock1对象 { Thread.Sleep(1000); //线程挂起1s lock (lock2) //试图获取lock2对象的锁定 { Console.WriteLine("成功获取到lock2对象的锁定"); } } } /// <summary> /// 造成一个死锁 /// </summary> public static void Test() { //定义两个锁定对象 object lock1 = new object(); object lock2 = new object(); //开启线程 Thread t1 = new Thread(() => { LockTooMuch(lock1, lock2); }); t1.Start(); //在主线程中锁定lock2对象 lock (lock2) { Console.WriteLine("这将要产生一个死锁"); Thread.Sleep(1000); lock (lock1) //试图访问lock1 { Console.WriteLine("成功获取到lock1对象的锁定"); } } } } }
这次死锁发生在Test()的lock(lock1)。
这个很容易理解,想使用lock1的引用时,但已经被LockTooMuch的lock(lock1)使用了这个引用。
而LockTooMuch想使用lcok2的引用,但已经被Test()引用了。
他们互相指望对方放弃引用。
下面谈谈解决办法:
改用Monitor.TryEnter方法进行锁,因为它提供超时机制。
程序执行效果如下:
/// <summary> /// 避免死锁 /// </summary> public static void Test2() { //定义两个锁定对象 object lock1 = new object(); object lock2 = new object(); //开启线程 Thread t1 = new Thread(() => { LockTooMuch(lock1, lock2); }); t1.Start(); //在主线程中使用Monitor类来锁定lock2对象 //在主线程中锁定lock2对象 lock (lock2) { Console.WriteLine("使用Monitor.TryEnter避免死锁,有一个超时时间的设定 超时返回false"); Thread.Sleep(1000); //设置5s的超时时间 if (Monitor.TryEnter(lock1, TimeSpan.FromSeconds(5))) { Console.WriteLine("成功获取到lock1对象的锁定"); } else { Console.WriteLine("获取lock1对象失败,"); } } }
但是如果程序结构有问题,即使靠超时这种外力推一把,下个循环周期过来会不会还是会死锁呢?
这个也是我们需要考虑的问题。
但是至少这种超时机制做为一种检测是否有死锁问题出现的手段还是可以的。
如果程序自己都不知道自己死锁了,就只能看个屏幕发呆了。
---------------------
作者:hackpig
来源:www.skcircle.com
版权声明:本文为博主原创文章,转载请附上博文链接!

