程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 多線程中如何避免死鎖

多線程中如何避免死鎖

編輯:關於.NET

死鎖是很討厭的(雖然活鎖更討厭),如何避免死鎖呢?

在兩個線程間的循環等待是比較容易識別的,但是在死 鎖的形成中如果包含多個線程,那麼就是難以發現的(現實中不少這種情況)。

首先來看看死鎖形成的幾個必要條 件

1、互斥

2、等待

3、不可搶占

4、循環等待

除了完全避免多線程編程之外,如果要 避免死鎖,那麼必須要使得上面這4個條件中有任意一個不滿足。

1、互斥是大多數鎖的一種固有性質,你沒辦法改 變它。

2、如果程序持有的鎖不會多於一個,那麼就不會發生死鎖問題。但是這通常也是不可能的。

3、不可 搶占,線程中斷和線程終止並非是實現這個功能的合適方式。

4、循環等待。通過在鎖上施加一種順序,並且要求線 程按照這種順序來獲取鎖。那麼就不會發生循環的獲取鎖操作,也就不會發生死鎖。這也是這四種條件中最容易消除的。

我們用一個簡單的例子實現簡單的鎖分級。

第一種會有並發問題的寫法,我們本意是通過形參fromAccount , toAccount 來表明鎖的順序,可這是做不到的。因此如果我們調換了一下傳入的實參的順序,就會產生死鎖問題。

class BankAccount 
    {
        public int id { get; set; }
        public decimal Balance { get; set; }
    
        public static void Transfer(BankAccount fromAccount, BankAccount toAccount, decimal amount)
        {
            lock (fromAccount)
            {
                if (fromAccount.Balance < amount)
                {
                    throw new Exception("Insufficient funds");
                }

                lock (toAccount)
                {
                    fromAccount.Balance -= amount;
                    toAccount.Balance += amount;
                }
            }
        }
    }

我們可以通過比較id來保證順序,同時這種寫法不會引入新的開銷。但是這種寫法局限性比較大,不通用。

class BankAccount
    {
        public int id { get; set; }
        public decimal Balance { get; set; }
    
        public static void SafeTransfer(BankAccount fromAccount, BankAccount toAccount, decimal amount)
        {
            if (fromAccount.id < toAccount.id)
            {
                lock (fromAccount)
                {
                    lock (toAccount)
                    {
                        transferMoney(fromAccount, toAccount, amount);
                    }
                }
            }
            else if (fromAccount.id > toAccount.id)
            {
                lock (toAccount)
                {
                    lock (fromAccount)
                    {
                        TransferMoney(fromAccount, toAccount, amount);
                    }
                }
            }
        }
    
    
        private static void TransferMoney(BankAccount fromAccount, BankAccount toAccount, decimal amount)
        {
            if (fromAccount.Balance < amount)
            {
                throw new Exception("Insufficient funds");
            }
    
            fromAccount.Balance -= amount;
            toAccount.Balance += amount;
        }
    
    }

本質上是通過固定一種順序,因此我們想到可以通過排序的方式使得可以接受多個鎖,並且更通用。

class MultiLocksHelper<T> where T :IComparable<T>
    {
        internal static void Enter(params T[] locks)
        {
            Array.Sort(locks);
    
            int i = 0;
            try
            {
                for (; i < locks.Length; i++)
                {
                    Monitor.Enter(locks[i]);
                }
            }
            catch
            {
                for(int j= i-1;j>0;j--)
                {
                    Monitor.Exit(locks[j]);
                }
                throw;
            }
        }
    
        internal static void Exit(params T[] locks)
        {
            Array.Sort(locks);
    
            for (int i = locks.Length - 1; i >= 0; i--)
            {
                Monitor.Exit(locks[i]);
            }
        }
    }

這時調用起來就很簡單了,只有幾行代碼。

class BankAccount : 

IComparable<BankAccount>
    {
        public int id { get; set; }
        public decimal Balance { get; set; }
    
        public static void GenericSafeTransfer(BankAccount fromAccount, BankAccount toAccount, decimal 

amount)
        {
            MultiLocksHelper<BankAccount>.Enter(fromAccount, toAccount);
    
            try
            {
                TransferMoney(fromAccount, toAccount, amount);
            }
            finally
            {
                MultiLocksHelper<BankAccount>.Exit(fromAccount, toAccount);
            }
    
        }
    
    
        private static void TransferMoney(BankAccount fromAccount, BankAccount toAccount, decimal amount)
        {
            if (fromAccount.Balance < amount)
            {
                throw new Exception("Insufficient funds");
            }
    
            fromAccount.Balance -= amount;
            toAccount.Balance += amount;
        }
    
    
        public int CompareTo(BankAccount other)
        {
            if (this.id > other.id)
                return 1;
            else
                return -1;
        }
    }

鎖分級的復雜性:

如果要為鎖指定級別,那麼就需要規劃並遵守一定的原則。我們很難從一開始就 提出很完備的鎖分級策略。也比較難估計哪些地方使用到鎖。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved