c#Lock學習筆記


轉自 http://www.cnblogs.com/tianma3798/p/6290158.html

參考官網  https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/lock-statement

一、lock關鍵詞說明

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

2. lock 語句塊鎖定,功能等同於

Monitor.Enter(obj); 
//代碼段
Monitor.Exit(obj);

3. lock語句塊鎖定和Monitor線程鎖,不能跨進程同步

二、備注

lock 關鍵字可確保當一個線程位於代碼的臨界區時,另一個線程不會進入該臨界區。 如果其他線程嘗試進入鎖定的代碼,則它將一直等待(即被阻止),直到該對象被釋放。

線程 這節討論了線程處理。

lock 關鍵字在塊的開始處調用 Enter,而在塊的結尾處調用 Exit。 ThreadInterruptedException 引發,如果 Interrupt 中斷等待輸入 lock 語句的線程。

通常,應避免鎖定 public 類型,否則實例將超出代碼的控制范圍。 常見的結構 lock (this)lock (typeof (MyType)) 和 lock ("myLock") 違反此准則:

  • 如果實例可以被公共訪問,將出現 lock (this) 問題。

  • 如果 MyType 可以被公共訪問,將出現 lock (typeof (MyType)) 問題。

  • 由於進程中使用同一字符串的任何其他代碼都將共享同一個鎖,所以出現 lock("myLock") 問題。

最佳做法是定義 private 對象來鎖定, 或 private static 對象變量來保護所有實例所共有的數據。

在 lock 語句的正文不能使用 等待 關鍵字。

三、特別說明

1.lock語句中鎖定的必須是引用類型的對象,不能是值類型

(1)值類型 一般都在線程函數自己的棧里,每個線程局部棧是不一樣的,互相之間不會有影響,所以不用鎖定
一個特例,引用類型值類型字段在堆里,但可以通過lock那個引用類型對象就可以實現了。

(2)引用可以指向同一個對象,而值類型的變量每次都是不同的

(3)他要的就是引用類型,如果你傳一個值類型,會裝箱,下次代碼運行到這里,又會裝箱,兩次不是同一個對象,所以鎖不住。這個解釋最靠譜

2.為了避免死鎖,lock的對象需要是private對象

3.為了避免lock對象的唯一性,通lock的對象為 private static或者 private readonly static

第二章,什么時候需要鎖

轉自 http://www.cnblogs.com/plin2008/archive/2009/06/24/1510057.html

      首先要理解鎖定是解決競爭條件的,也就是多個線程同時訪問某個資源,造成意想不到的結果。比如,最簡單的情況是,一個計數器,兩個線程 同時加一,后果就是損失了一個計數,但相當頻繁的鎖定又可能帶來性能上的消耗,還有最可怕的情況死鎖。那么什么情況下我們需要使用鎖,什么情況下不需要 呢?

      1)只有共享資源才需要鎖定
      只有可以被多線程訪問的共享資源才需要考慮鎖定,比如靜態變量,再比如某些緩存中的值,而屬於線程內部的變量不需要鎖定。 

      2)多使用lock,少用Mutex
      如果你一定要使用鎖定,請盡量不要使用內核模塊的鎖定機制,比如.NET的Mutex,Semaphore,AutoResetEvent和 ManuResetEvent,使用這樣的機制涉及到了系統在用戶模式和內核模式間的切換,性能差很多,但是他們的優點是可以跨進程同步線程,所以應該清 楚的了解到他們的不同和適用范圍。

      3)了解你的程序是怎么運行的
      實際上在web開發中大多數邏輯都是在單個線程中展開的,一個請求都會在一個單獨的線程中處理,其中的大部分變量都是屬於這個線程的,根本沒有必要考慮鎖定,當然對於ASP.NET中的Application對象中的數據,我們就要考慮加鎖了。

      4)把鎖定交給數據庫
      數 據庫除了存儲數據之外,還有一個重要的用途就是同步,數據庫本身用了一套復雜的機制來保證數據的可靠和一致性,這就為我們節省了很多的精力。保證了數據源 頭上的同步,我們多數的精力就可以集中在緩存等其他一些資源的同步訪問上了。通常,只有涉及到多個線程修改數據庫中同一條記錄時,我們才考慮加鎖。 

      5)業務邏輯對事務和線程安全的要求
      這 條是最根本的東西,開發完全線程安全的程序是件很費時費力的事情,在電子商務等涉及金融系統的案例中,許多邏輯都必須嚴格的線程安全,所以我們不得不犧牲 一些性能,和很多的開發時間來做這方面的工作。而一般的應用中,許多情況下雖然程序有競爭的危險,我們還是可以不使用鎖定,比如有的時候計數器少一多一, 對結果無傷大雅的情況下,我們就可以不用去管它。

三、小題練練

轉自 http://www.fx114.net/qa-276-77022.aspx

看看以下會不會產生死鎖:

public class A
    {
        private object obj = new object();
        public void Test(int i)
        {
            lock (obj)
            {
                if (i>10)
                {
                    i--;
                    Test(i);
                }
                else
                {
                    Console.WriteLine(i);
                }
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            a.Test(15);
            Console.ReadKey();
        }
    }

 

你的答案是會產生死鎖嗎?可以理解,因為也許你以前一直以為外層遞歸被鎖住后,不允許在訪問里邊的代碼快,而由於遞歸,有需要等待外層遞歸解鎖,所以由此造成死鎖,現在才知道這種理解是錯誤的;結果是永遠不會出現死鎖,因為lock的本質是對於不同的線程來說的,或者這個所的所有權已經歸該現成所有,它可以任意使用該鎖

說道這里又想多說一點,假如另一個線程在另一個函數里操作同一個obj,這是obj可以被訪問嗎在鎖釋放之前,如下代碼:

public class Test
    {
        string name = "Empty";

        public Test()
        {
 
        }

        public string Name
        {
            get
            {
                return this.name;
            }
            set
            {
                this.name = value;
            }
        }
    }

    public class A
    {
        Test test = new Test();

        public void OneMethod()
        {
            lock (this.test)
            {
                Console.WriteLine("one thread come in,sleep for 10 seconds");
                Console.WriteLine(test.Name);
                Thread.Sleep(10000);//如果第二個線程可以在鎖住期間可以對test對象操作,那么線程二應該在10000毫秒之內已經輸出
                test.Name = "One thread output";
                Console.WriteLine(test.Name);
            }
        }

        public void TwoMethod()
        {
            Thread.Sleep(100);//為了確保第一個線程先鎖住后在操作test對象
            Console.WriteLine("two thread come in");//假如由於線程一鎖住test對象而不能對test操作的話,那么要等待10000毫秒
            test.Name = "Two thread output";
            Console.WriteLine(test.Name);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            Thread one = new Thread(new ThreadStart(a.OneMethod));
            Thread two = new Thread(new ThreadStart(a.TwoMethod));
            one.Start();
            two.Start();
            Console.ReadKey();
        }
    }

其輸出結果為:

one thread come in,sleep for 10 seconds

Empty

two thread come in//在第一個線程之間操作

Two thread output//在第一個線程鎖住test對象之中,改變的test。Name屬性值,說明鎖中test時將可以對test進行操作

One thread output

 

答案是:test對象在別的函數中被鎖中時,別的線程在被鎖塊外是可以對test進行操作的

假如我把第二個方法代碼做如下修改:

public void TwoMethod()
        {
            Thread.Sleep(100);//為了確保第一個線程先鎖住后在操作test對象
            Console.WriteLine("two thread come in");//假如由於線程一鎖住test對象而不能對test操作的話,那么要等待10000毫秒
            lock (this.test)
            {
                test.Name = "Two thread output";
                Console.WriteLine(test.Name);
            }            
        }

這時的結果是:

one thread come in,sleep for 10 seconds

Empty

two thread come in//在第一個線程獲得鎖之間線程二進入了第二方法中的鎖之前操作

One thread output//

Twothread output//說明鎖test被解鎖以后才執行

答案是:一個類中幾個方法鎖住了同一個對象(該對象必須是引用類型)時,那么第一個獲得鎖的線程操作,其他線程走到鎖對象(哪怕所對象不是在同一個函數中)之前將等待對象被解鎖,解鎖后才可以進入。打個比喻:test對象相當於一個響應器,lock()鎖相當於一個鎖,操作系統相當於一個監控器,而一個或多個函數中的鎖塊相當於一個或多大門,多個線程相當於等在一個或多個大門前的人。假如有任一一個大門被一個人闖進時,那么監控器立即給所有鎖上鎖lock,鎖定所有大門,不准任何人進,直到該人出來,監控器自動打開所有鎖,所有大門打開,重復執行第一步,監控器保證只有一個人進入某一個大門。當然如果某一個響應器沒有lock,就相當於大門沒有鎖,就可以對test操作,這種情況就是上一種現象。

 

二、一道面試題(目前我也不太明白)

轉自 https://www.cnblogs.com/myshell/archive/2010/07/18/1780386.html

和https://q.cnblogs.com/q/47290/

public void test(int i) 
 { 
    lock(this) 
   { 
      if (i > 10) 
     { 
          i--; 
          test(i); 
      } 
    } 
 }

分析一下代碼,當調用test方法時i>10時是否會引起死鎖

不會。終於在《CLR via C#》第二版(中文版,清華大學出版社出版)的第530頁中第7行找到了這樣的描述:“同樣需要引起注意的是線程可以遞歸擁有同步塊”。即同一線程可以遞歸調用lock語句,這是單線程操作,該線程本身就是鎖的擁有者,當再次進入時,自然能獲取到進入該鎖塊的權利。

線程重入問題,lock會與線程關聯,如關聯上A1線程,那么A1線程重復進入多次也是OK的。但是lock又是獨占鎖,所以一旦關聯上線程,別的線程就無法進入lock代碼塊。

多線程操作時,會阻塞等待。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM