C# Lock關鍵字


 

lock 關鍵字將語句塊標記爲臨界區,方法是獲取給定對象的互斥鎖,執行語句,然後釋放該鎖。

 

lock語句根本使用的就是Monitor.Enter和Monitor.Exit,也就是說lock(this)時執行Monitor.Enter(this),大括號結束時執行Monitor.Exit(this).他的意義在於什麼呢,對於任何一個對象來說,他在內存中的第一部分放置的是所有方法的地址,第二部分放着一個索引,他指向CLR中的SyncBlock Cache區域中的一個SyncBlock.什麼意思呢?就是說,當你執行Monitor.Enter(Object)時,如果object的索引值爲負數,就從SyncBlock Cache中選區一個SyncBlock,將其地址放在object的索引中。這樣就完成了以object爲標誌的鎖定,其他的線程想再次進行Monitor.Enter(object)操作,將獲得object爲正數的索引,然後就等待。直到索引變爲負數,即線程使用Monitor.Exit(object)將索引變爲負數。


使用lock需要注意的地方:

1.lock不能鎖定空值某一對象可以指向Null,但Null是不需要被釋放的。(請參考:認識全面的null)
2.lock不能鎖定string類型,雖然它也是引用類型的。因爲字符串類型被CLR“暫留”

這意味着整個程序中任何給定字符串都只有一個實例,就是這同一個對象表示了所有運行的應用程序域的所有線程中的該文本。因此,只要在應用程序進程中的任何位置處具有相同內容的字符串上放置了鎖,就將鎖定應用程序中該字符串的所有實例。因此,最好鎖定不會被暫留的私有或受保護成員。
3.lock鎖定的對象是一個程序塊的內存邊界
4.值類型不能被lock,因爲前文標紅字的“對象被釋放”,值類型不是引用類型的

5.lock就避免鎖定public 類型或不受程序控制的對象。
例如,如果該實例可以被公開訪問,則 lock(this) 可能會有問題,因爲不受控制的代碼也可能會鎖定該對象。這可能導致死鎖,即兩個或更多個線程等待釋放同一對象。出於同樣的原因,鎖定公共數據類型(相比於對象)也可能導致問題。
使用lock(this)的時候,類的成員變量的值可能會被不在臨界區的方法改值了

 

應用場景:經常會應用於防止多線程操作導致公用變量值出現不確定的異常,用於確保操作的安全性

 

示例


// statements_lock2.cs
using System;
using System.Threading;

class Account
{
    private Object thisLock = new Object();
    int balance;

    Random r = new Random();

    public Account(int initial)
    {
        balance = initial;
    }

    int Withdraw(int amount)
    {

        // This condition will never be true unless the lock statement
        // is commented out:
        if (balance < 0)
        {
            throw new Exception("Negative Balance");
        }

        // Comment out the next line to see the effect of leaving out 
        // the lock keyword:
        lock(thisLock)
        {
            if (balance >= amount)
            {
                Console.WriteLine("Balance before Withdrawal :  " + balance);
                Console.WriteLine("Amount to Withdraw        : -" + amount);
                balance = balance - amount;
                Console.WriteLine("Balance after Withdrawal  :  " + balance);
                return amount;
            }
            else
            {
                return 0; // transaction rejected
            }
        }
    }

    public void DoTransactions()
    {
        for (int i = 0; i < 100; i++)
        {
            Withdraw(r.Next(1, 100));
        }
    }
}

class Test
{
    static void Main()
    {
        Thread[] threads = new Thread[10];
        Account acc = new Account(1000);
        for (int i = 0; i < 10; i++)
        {
            Thread t = new Thread(new ThreadStart(acc.DoTransactions));
            threads[i] = t;
        }
        for (int i = 0; i < 10; i++)
        {
            threads[i].Start();
        }
    }
}

 

 

以上圖片由“圖鬥羅”提供

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章