死鎖的產生
什么是死鎖:
所謂死鎖,是指多個進程在運行過程中因爭奪資源而造成的一種僵局,當進程處於這種僵持狀態時,若無外力作用,它們都將無法再向前推進。 因此我們舉個例子來描述,如果此時有一個線程A,按照先鎖a再獲得鎖b的的順序獲得鎖,而在此同時又有另外一個線程B,按照先鎖b再鎖a的順序獲得鎖。如下圖所示
產生死鎖的原因
競爭資源
系統中的資源可以分為兩類:
- 競爭不可剝奪資源(例如:系統中只有一台打印機,可供進程P1使用,假定P1已占用了打印機,若P2繼續要求打印機打印將阻塞)
- 是競爭臨時資源(臨時資源包括硬件中斷、信號、消息、緩沖區內的消息等),通常消息通信順序進行不當,則會產生死鎖
進程間推進順序非法
1. 若P1保持了資源R1,P2保持了資源R2,系統處於不安全狀態,因為這兩個進程再向前推進,便可能發生死鎖
2. 例如,當P1運行到P1:Request(R2)時,將因R2已被P2占用而阻塞;當P2運行到P2:Request(R1)時,也將因R1已被P1占用而阻塞,於是發生進程死鎖
一個死鎖的例子
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對象的鎖定"); } } }
通過Monitor的超時鎖定機制避免死鎖
直接使用Monitor類。其擁有TryEnter方法,該方法接受一個超時參數。如果在我們能夠獲取被lock保護的資源之前,超時參數過期,則該方法會返回 false.
/// <summary> /// 使用Monitor避免死鎖 /// </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對象失敗,"); } } }
源自:C#多線程學習總結