首頁 > 軟體

C#使用LOCK實現執行緒同步

2022-04-19 19:02:09

一、簡介

執行緒安全概念:執行緒安全是指在當一個執行緒存取該類的某個資料時,進行保護,其他執行緒不能進行存取直到該執行緒讀取完,其他執行緒才可使用。不會出現資料不一致或者資料汙染。

執行緒有可能和其他執行緒共用一些資源,比如,記憶體,檔案,資料庫等。當多個執行緒同時讀寫同一份共用資源的時候,可能會引起衝突。這時候,我們需要引入執行緒“同步”機制,即各位執行緒之間要有個先來後到,不能一窩蜂擠上去搶作一團。執行緒同步的真實意思和字面意思恰好相反。執行緒同步的真實意思,其實是“排隊”:幾個執行緒之間要排隊,一個一個對共用資源進行操作,而不是同時進行操作。

二、程式碼

下面將通過簡單的四個案例進行對比,來講解LOCK的實現執行緒同步使用。

案例一:

首先建立兩個執行緒,兩個執行緒執行同一個方法,程式碼如下:

class Program
    {
        static void Main(string[] args)
        {
            Thread threadA = new Thread(ThreadMethod); //執行的必須是無返回值的方法 
            threadA.Name = "threadA";
            Thread threadB = new Thread(ThreadMethod); //執行的必須是無返回值的方法 
            threadB.Name = "threadB";
            threadA.Start();
            threadB.Start();
            Console.ReadKey();
        }
        public static void ThreadMethod(object parameter)
        {
            for (int i = 1; i <= 10; i++)
            {
                Console.WriteLine("我是:{0},我迴圈{1}次", Thread.CurrentThread.Name, i);
                Thread.Sleep(1000);//休眠一秒
            }
        }
    }

通過下面的執行結果,可以很清楚的看到,兩個執行緒是在同時執行ThreadMethod這個方法,這顯然不符合我們執行緒同步的要求。

執行結果:

案例二:

通過對上面程式碼的修改如下:

class Program
    {
        static void Main(string[] args)
        {
            Program pro = new Program();
            Thread threadA = new Thread(pro.ThreadMethod); 
            threadA.Name = "threadA";
            Thread threadB = new Thread(pro.ThreadMethod); 
            threadB.Name = "threadB";
            threadA.Start();
            threadB.Start();
            Console.ReadKey();
        }
        public void ThreadMethod(object parameter)
        {
            lock (this) //新增lock關鍵字
            {
                for (int i = 1; i <= 10; i++)
                {
                    Console.WriteLine("我是:{0},我迴圈{1}次", Thread.CurrentThread.Name, i);
                    Thread.Sleep(1000);//休眠一秒
                }
            }
        }
    }

執行結果:

 

我們通過新增了 lock(this) {...}程式碼,檢視執行結果實現了我們想要的執行緒同步需求。

案例三:

但是我們知道this表示當前類範例的本身,那麼有這麼一種情況,我們把需要存取的方法所在的型別進行兩個範例A和B,執行緒A存取範例A的方法ThreadMethod,執行緒B存取範例B的方法ThreadMethod,這樣的話還能夠達到執行緒同步的需求嗎?

修改後的程式碼如下:

class Program
    {
        static void Main(string[] args)
        {
            Program pro1 = new Program();
            Program pro2 = new Program();
            Thread threadA = new Thread(pro1.ThreadMethod); 
            threadA.Name = "threadA";
            Thread threadB = new Thread(pro2.ThreadMethod); 
            threadB.Name = "threadB";
            threadA.Start();
            threadB.Start();
            Console.ReadKey();
        }
        public void ThreadMethod(object parameter)
        {
            lock (this) //新增lock關鍵字
            {
                for (int i = 1; i <= 10; i++)
                {
                    Console.WriteLine("我是:{0},我迴圈{1}次", Thread.CurrentThread.Name, i);
                    Thread.Sleep(1000);//休眠一秒
                }
            }
        }
    }

執行結果:

我們會發現,執行緒又沒有實現同步了!lock(this)對於這種情況是不行的!

案例四:

通過對上面程式碼再次進行如下修改:

class Program
    {
        private static object obj = new object();
        static void Main(string[] args)
        {
            Program pro1 = new Program();
            Program pro2 = new Program();
            Thread threadA = new Thread(pro1.ThreadMethod); 
            threadA.Name = "threadA";
            Thread threadB = new Thread(pro2.ThreadMethod); 
            threadB.Name = "threadB";
            threadA.Start();
            threadB.Start();
            Console.ReadKey();
        }
        public void ThreadMethod(object parameter)
        {
            lock (obj) //新增lock關鍵字
            {
                for (int i = 1; i <= 10; i++)
                {
                    Console.WriteLine("我是:{0},我迴圈{1}次", Thread.CurrentThread.Name, i);
                    Thread.Sleep(1000);//休眠一秒
                }
            }
        }
    }

執行結果:

通過檢視執行結果。會發現程式碼實現了我們的需求。

那麼 lock(this) 和lock(Obj)有什麼區別呢?

lock(this) 鎖定 當前範例物件,如果有多個類範例的話,lock鎖定的只是當前類範例,對其它類範例無影響。所有不推薦使用。
lock(typeof(Model))鎖定的是model類的所有範例。
lock(obj)鎖定的物件是全域性的私有化靜態變數。外部無法對該變數進行存取。
lock 確保當一個執行緒位於程式碼的臨界區時,另一個執行緒不進入臨界區。如果其他執行緒試圖進入鎖定的程式碼,則它將一直等待(即被阻止),直到該物件被釋放。
所以,lock的結果好不好,還是關鍵看鎖的誰,如果外邊能對這個誰進行修改,lock就失去了作用。所以一般情況下,使用私有的、靜態的並且是唯讀的物件

三、總結

  • 1.lock的是必須是參照型別的物件,string型別除外。
  • 2.lock推薦的做法是使用靜態的、唯讀的、私有的物件。
  • 3.保證lock的物件在外部無法修改才有意義,如果lock的物件在外部改變了,對其他執行緒就會暢通無阻,失去了lock的意義。

不能鎖定字串,鎖定字串尤其危險,因為字串被公共語言執行庫 (CLR)“暫留”。 這意味著整個程式中任何給定字串都只有一個範例,就是這同一個物件表示了所有執行的應用程式域的所有執行緒中的該文字。因此,只要在應用程式程序中的任何位置處具有相同內容的字串上放置了鎖,就將鎖定應用程式中該字串的所有範例。通常,最好避免鎖定 public 型別或鎖定不受應用程式控制的物件範例。例如,如果該範例可以被公開存取,則 lock(this) 可能會有問題,因為不受控制的程式碼也可能會鎖定該物件。這可能導致死鎖,即兩個或更多個執行緒等待釋放同一物件。出於同樣的原因,鎖定公共資料型別(相比於物件)也可能導致問題。而且lock(this)只對當前物件有效,如果多個物件之間就達不到同步的效果。lock(typeof(Class))與鎖定字串一樣,範圍太廣了。

到此這篇關於C#使用LOCK實現執行緒同步的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com