[C#.NET][Thread] 執行緒的互鎖與死鎖

  • 11373
  • 0
  • 2013-08-16

[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釋放

image

 

接下來就來實作一下怎麼打死結


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);
}

 

執行結果就是兩個互相等待…程式不會結束

image

 

解決死結的方法很簡單,只要大家都用一樣的鎖定順序就不會變死結了,在原本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);
}

image

 

同樣的寫法若換成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); 

    }
}

 

 

image

Deadlock.zip

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo