[C#.NET][Thread] 執行緒的互鎖與死鎖
所謂死結是由兩條執行緒(或者更多),因互相等待而造成了死結。
互鎖是表示兩條執行緒中,一次只有一條執行在工作,另一條在等待,這如果沒弄好很容易弄巧成拙變成死結。
public static void MethodA()
{
lock (_LockA)
{
//TODO...
lock (_LockB)
{
//TODO...
}
}
}
public static void MethodB()
{
lock (_LockB)
{
//TODO...
lock (_LockA)
{
//TODO...
}
}
}
上面是一個死結寫法,MethodA鎖了_LockA,MethodB鎖了_LockB;MethodA裡的方法在等_LockB釋放,MethodB裡的方法在等_LockA釋放
接下來就來實作一下怎麼打死結
static object _LockA = new object();
static object _LockB = new object();
static void Main(string[] args)
{
Thread threadA = new Thread(new ThreadStart(MethodA));
Thread threadB = new Thread(new ThreadStart(MethodB));
threadB.Start();
threadA.Start();
//主執行緒等待其他執行緒完成工作
threadB.Join();
threadA.Join();
Console.WriteLine("Main Thread Exit");
Console.ReadKey();
}
public static void MethodA()
{
Thread t = Thread.CurrentThread;
lock (_LockA)
{
Console.WriteLine("Thread[{0}]:Enter MethodA, _LockA be Locked ,State:{1}", t.ManagedThreadId, t.ThreadState);
lock (_LockB)
{
Console.WriteLine("Thread[{0}]:Enter MethodA ,_LockB be Locked ,State:{1}", t.ManagedThreadId, t.ThreadState);
}
Console.WriteLine("Thread[{0}]:Leave MethodA ,State:{1}", t.ManagedThreadId, t.ThreadState);
}
}
public static void MethodB()
{
Thread t = Thread.CurrentThread;
lock (_LockB)
{
Console.WriteLine("Thread[{0}]:Enter MethodB, _LockB be Locked ,State:{1}", t.ManagedThreadId, t.ThreadState);
lock (_LockA)
{
Console.WriteLine("Thread[{0}]:Enter MethodB, _LockA be Locked ,State:{1}", t.ManagedThreadId, t.ThreadState);
}
}
Console.WriteLine("Thread[{0}]:Leave MethodB ,State:{1}", t.ManagedThreadId, t.ThreadState);
}
執行結果就是兩個互相等待…程式不會結束
解決死結的方法很簡單,只要大家都用一樣的鎖定順序就不會變死結了,在原本MethodB裡的鎖定順序變成跟MethodA一樣就好了
public static void MethodB()
{
Thread t = Thread.CurrentThread;
lock (_LockA)
{
Console.WriteLine("Thread[{0}]:Enter MethodB, _LockA be Locked ,State:{1}", t.ManagedThreadId, t.ThreadState);
lock (_LockB)
{
Console.WriteLine("Thread[{0}]:Enter MethodB, _LockB be Locked ,State:{1}", t.ManagedThreadId, t.ThreadState);
}
}
Console.WriteLine("Thread[{0}]:Leave MethodB ,State:{1}", t.ManagedThreadId, t.ThreadState);
}
同樣的寫法若換成Monitor類別,下面的寫法也是死結,要解決很簡單,記住,鎖定順序都要一樣就不會有死結
public static void MethodC()
{
Monitor.Enter(_LockC);
try
{
try
{
Monitor.Enter(_LockD);
}
catch (Exception) { }
finally
{
Monitor.Exit(_LockD);
}
}
catch (Exception) { }
finally
{
Monitor.Exit(_LockC);
}
}
public static void MethodD()
{
Monitor.Enter(_LockD);
try
{
try
{
Monitor.Enter(_LockC);
}
catch (Exception) { }
finally
{
Monitor.Exit(_LockC);
}
}
catch (Exception e) { }
finally
{
Monitor.Exit(_LockD);
}
}
非死結的完整程式碼
public static void MethodC()
{
Thread t = Thread.CurrentThread;
Monitor.Enter(_LockC);
try
{
Console.WriteLine("Thread[{0}]:Enter MethodC, _LockC be Locked ,State:{1}", t.ManagedThreadId, t.ThreadState);
try
{
Monitor.Enter(_LockD);
Console.WriteLine("Thread[{0}]:Enter MethodC, _LockD be Locked ,State:{1}", t.ManagedThreadId, t.ThreadState);
}
catch (Exception) { }
finally
{
Monitor.Exit(_LockD);
}
}
catch (Exception) { }
finally
{
Monitor.Exit(_LockC);
Console.WriteLine("Thread[{0}]:Leave MethodC ,State:{1}", t.ManagedThreadId, t.ThreadState);
}
}
public static void MethodD()
{
Thread t = Thread.CurrentThread;
Monitor.Enter(_LockC);
try
{
Console.WriteLine("Thread[{0}]:Enter MethodD, _LockD be Locked ,State:{1}", t.ManagedThreadId, t.ThreadState);
try
{
Monitor.Enter(_LockD);
Console.WriteLine("Thread[{0}]:Enter MethodD, _LockC be Locked ,State:{1}", t.ManagedThreadId, t.ThreadState);
}
catch (Exception) { }
finally
{
Monitor.Exit(_LockD);
}
}
catch (Exception) { }
finally
{
Monitor.Exit(_LockC);
Console.WriteLine("Thread[{0}]:Leave MethodD ,State:{1}", t.ManagedThreadId, t.ThreadState);
}
}
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET