多線程之旅:避免死結——簡單的鎖分級(鎖排序)

來源:互聯網
上載者:User

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

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

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

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

 

 鎖分級的複雜性:

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

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.