溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C#多線程鎖lock和Monitor怎么用

發布時間:2022-02-14 09:30:43 來源:億速云 閱讀:182 作者:iii 欄目:開發技術

本文小編為大家詳細介紹“C#多線程鎖lock和Monitor怎么用”,內容詳細,步驟清晰,細節處理妥當,希望這篇“C#多線程鎖lock和Monitor怎么用”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。

1,Lock

lock 用于讀一個引用類型進行加鎖,同一時刻內只有一個線程能夠訪問此對象。lock 是語法糖,是通過 Monitor 來實現的。

Lock 鎖定的對象,應該是靜態的引用類型(字符串除外)。

實際上字符串也可以作為鎖的對象使用,只是由于字符串對象的特殊性,可能會造成不同位置的不同線程沖突。
如果你能保證字符串的唯一性,例如 Guid 生成的字符串,也是可以作為鎖的對象使用的(但不建議)。 
鎖的對象也不一定要靜態才行,也可以通過類實例的成員變量,作為鎖對象。

lock 原型

lock 是 Monitor 的語法糖,生成的代碼對比:

lock (x)
{
    // Your code...
}
object __lockObj = x;
bool __lockWasTaken = false;
try
{
    System.Threading.Monitor.Enter(__lockObj, ref __lockWasTaken);
    // Your code...
}
finally
{
    if (__lockWasTaken) System.Threading.Monitor.Exit(__lockObj);
}

這里先不理會 Monitor,后面再說。

lock 編寫實例

首先,如果像下面這樣寫的話,拉出去打 si 吧。

        public void MyLock()
        {
            object o = new object();
            lock (o)
            {
            // 
            }
        }

下面編寫一個簡單的鎖,示例如下:

    class Program
    {
        private static object obj = new object();
        private static int sum = 0;
        static void Main(string[] args)
        {

            Thread thread1 = new Thread(Sum1);
            thread1.Start();
            Thread thread2 = new Thread(Sum2);
            thread2.Start();
            while (true)
            {
                Console.WriteLine($"{DateTime.Now.ToString()}:" + sum);
                Thread.Sleep(TimeSpan.FromSeconds(1));
            }
        }

        public static void Sum1()
        {
            sum = 0;
            lock (obj)
            {
                for (int i = 0; i < 10; i++)
                {
                    sum += i;
                    Console.WriteLine("Sum1");
                    Thread.Sleep(TimeSpan.FromSeconds(2));
                }
            }
        }

        public static void Sum2()
        {
            sum = 0;
            lock (obj)
            {
                for (int i = 0; i < 10; i++)
                {
                    sum += 1;
                    Console.WriteLine("Sum2");
                    Thread.Sleep(TimeSpan.FromSeconds(2));
                }
            }
        }
    }

類將自己設置為鎖, 這可以防止惡意代碼對公共對象采用做鎖。

例如:

  public void Access()
    {
        lock(this) {}
     }

鎖可以阻止其它線程執行鎖塊(lock(o){})中的代碼,當鎖定時,其它線程必須等待鎖中的線程執行完成并釋放鎖。但是這可能會給程序帶來性能影響。
鎖不太適合I/O場景,例如文件I/O,繁雜的計算或者操作比較持久的過程,會給程序帶來很大的性能損失。

10 種優化鎖的性能方法: http://www.thinkingparallel.com/2007/07/31/10-ways-to-reduce-lock-contention-in-threaded-programs/

2,Monitor

此對象提供同步訪問對象的機制;Monotor 是一個靜態類型,其方法比較少,常用方法如下:

操作說明
Enter, TryEnter獲取對象的鎖。 此操作還標記關鍵節的開頭。 其他任何線程都不能輸入臨界區,除非它使用不同的鎖定對象執行臨界區中的說明。
Wait釋放對象的鎖,以允許其他線程鎖定并訪問對象。 調用線程會等待另一個線程訪問對象。 使用脈沖信號通知等待線程關于對象狀態的更改。
Pulse 、PulseAll將信號發送到一個或多個等待線程。 信號通知等待線程:鎖定對象的狀態已更改,鎖的所有者已準備好釋放該鎖。 正在等待的線程置于對象的就緒隊列中,因此它可能最終接收對象的鎖。 線程鎖定后,它可以檢查對象的新狀態,以查看是否已達到所需的狀態。
Exit釋放對象的鎖。 此操作還標記受鎖定對象保護的臨界區的結尾。

怎么用呢

下面是一個很簡單的示例:

        private static object obj = new object();
        private static bool acquiredLock = false;
		
		public static void Test()
        {
            try
            {
                Monitor.Enter(obj, ref acquiredLock);
            }
            catch { }
            finally
            {
                if (acquiredLock)
                    Monitor.Exit(obj);
            }
        }

Monitor.Enter 鎖定 obj 這個對象,并且設置 acquiredLock 為 true,告訴別人 obj 已經被鎖定。

最后結束時,判斷 acquiredLock ,釋放鎖,并設置 acquiredLock 為 false。

解釋一下

臨界區:指被某些符號包圍的范圍。例如 {} 內。

Monitor 對象的 Enter 和 Exit 方法來標記臨界區的開頭和結尾。

Enter() 方法獲取鎖后,能夠保證只有單個線程能夠使用臨界區中的代碼。使用 Monitor 類,最好搭配 try{...}catch{...}finally{...} 來使用,因為如果獲取到鎖但是沒有釋放鎖的話,會導致其它線程無限阻塞,即發生死鎖。

一般來說,lock 關鍵字夠用了。

示例

下面示范了多個線程如何使用 Monitor 來實現鎖:

       private static object obj = new object();
        private static bool acquiredLock = false;
        static void Main(string[] args)
        {
            new Thread(Test1).Start();
            Thread.Sleep(1000);
            new Thread(Test2).Start();
        }

        public static void Test1()
        {
            try
            {
                Monitor.Enter(obj, ref acquiredLock);
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine("Test1正在鎖定資源");
                    Thread.Sleep(1000);
                }

            }
            catch { }
            finally
            {
                if (acquiredLock)
                    Monitor.Exit(obj);
                Console.WriteLine("Test1已經釋放資源");
            }
        }
        public static void Test2()
        {
            bool isGetLock = false;
            Monitor.Enter(obj);
            try
            {
                Monitor.Enter(obj, ref acquiredLock);
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine("Test2正在鎖定資源");
                    Thread.Sleep(1000);
                }

            }
            catch { }
            finally
            {
                if (acquiredLock)
                    Monitor.Exit(obj);
                Console.WriteLine("Test2已經釋放資源");
            }
        }

設置獲取鎖的時效

如果對象已經被鎖定,另一個線程使用 Monitor.Enter 對象,就會一直等待另一個線程解除鎖定。

但是,如果一個線程發生問題或者出現死鎖的情況,鎖一直被鎖定呢?或者線程具有時效性,超過一段時間不執行,已經沒有了意義呢?

我們可以通過 Monitor.TryEnter() 來設置等待時間,超過一段時間后,如果鎖還沒有釋放,就會返回 false。

改造上面的示例如下:

        private static object obj = new object();
        private static bool acquiredLock = false;
        static void Main(string[] args)
        {
            new Thread(Test1).Start();
            Thread.Sleep(1000);
            new Thread(Test2).Start();
        }

        public static void Test1()
        {
            try
            {
                Monitor.Enter(obj, ref acquiredLock);
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine("Test1正在鎖定資源");
                    Thread.Sleep(1000);
                }
            }
            catch { }
            finally
            {
                if (acquiredLock)
                    Monitor.Exit(obj);
                Console.WriteLine("Test1已經釋放資源");
            }
        }
        public static void Test2()
        {
            bool isGetLock = false;
            isGetLock = Monitor.TryEnter(obj, 500);
            if (isGetLock == false)
            {
                Console.WriteLine("鎖還沒有釋放,我不干活了");
                return;
            }
            try
            {
                Monitor.Enter(obj, ref acquiredLock);
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine("Test2正在鎖定資源");
                    Thread.Sleep(1000);
                }
            }
            catch { }
            finally
            {
                if (acquiredLock)
                    Monitor.Exit(obj);
                Console.WriteLine("Test2已經釋放資源");
            }
        }

讀到這里,這篇“C#多線程鎖lock和Monitor怎么用”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女