C# 樂觀鎖、悲觀鎖、共享鎖、排它鎖、互斥鎖


悲觀鎖(Pessimistic Lock), 顧名思義,就是很悲觀,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會block直到它拿到鎖。傳統的關系型數據庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。 通過 jdbc 實現時 sql 語句只要在整個語句之后加 for update 即可。例如: select …for update

樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿數據的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號等機制。樂觀鎖適用於多讀的應用類型,這樣可以提高吞吐量,像數據庫如果提供類似於write_condition機制的其實都是提供的樂觀鎖。

兩種鎖各有優缺點,不可認為一種好於另一種,像樂觀鎖適用於寫比較少的情況下,即沖突真的很少發生的時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。但如果經常產生沖突,上層應用會不斷的進行retry,這樣反倒是降低了性能,所以這種情況下用悲觀鎖就比較合適

 

共享鎖:(讀取)操作創建的鎖。其他用戶可以並發讀取數據,但任何事物都不能獲取數據上的排它鎖,直到已釋放所有共享鎖。
共享鎖(S鎖)又稱為讀鎖,若事務T對數據對象A加上S鎖,則事務T只能讀A;其他事務只能再對A加S鎖,而不能加X鎖,直到T釋放A上的S鎖。這就保證了其他事務可以讀A,但在T釋放A上的S鎖之前不能對A做任何修改。

共享鎖的使用

在第一個連接中執行以下語句
begin tran
select * from table1 holdlock -holdlock人為加鎖
where B='b2'
waitfor delay '00:00:30' --等待30秒
commit tran
在第二個連接中執行以下語句
begin tran
select A,C from table1
where B='b2'
update table1
set A='aa'
where B='b2'
commit tran
若同時執行上述兩個語句,則第二個連接中的select查詢可以執行
而update必須等待第一個事務釋放共享鎖轉為排它鎖后才能執行 即要等待30秒
 
排它鎖:排它鎖又稱為寫鎖((eXclusive lock,簡記為X鎖)),若事物T對數據對象A加上X鎖,則只允許T讀取和修改A,其它任何事務都不能再對A加任何類型的鎖,直到T釋放A上的鎖。它防止任何其它事務獲取資源上的鎖,直到在事務的末尾將資源上的原始鎖釋放為止。

排它鎖的使用

在第一個連接中執行以下語句
begin tran
update table1
set A='aa'
where B='b2'
waitfor delay '00:00:30' --等待30秒
commit tran
在第二個連接中執行以下語句
begin tran
select * from table1
where B='b2'
commit tran

若同時執行上述兩個語句,則select查詢必須等待update執行完畢才能執行即要等待30秒

 

互斥鎖(Mutex)

互斥鎖是一個互斥的同步對象,意味着同一時間有且僅有一個線程可以獲取它。

互斥鎖可適用於一個共享資源每次只能被一個線程訪問的情況

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
using System.Threading;
 
namespace MyTTCon
{
    class shareRes
    {
        public static int count = 0;
        public static Mutex mutex = new Mutex();
    }
 
    class IncThread
    {
        int number;
        public Thread thrd;
        public IncThread(string name, int n)
        {
            thrd = new Thread(this.run);
            number = n;
            thrd.Name = name;
            thrd.Start();
        }
        void run()
        {
            Console.WriteLine(thrd.Name + "正在等待 the mutex");
            //申請
            shareRes.mutex.WaitOne();
            Console.WriteLine(thrd.Name + "申請到 the mutex");
            do
            {
                Thread.Sleep(1000);
                shareRes.count++;
                Console.WriteLine("In " + thrd.Name + "ShareRes.count is " + shareRes.count);
                number--;
            } while (number > 0);
            Console.WriteLine(thrd.Name + "釋放 the nmutex");
            //  釋放
            shareRes.mutex.ReleaseMutex();
        }
    }
    class DecThread
    {
        int number;
        public Thread thrd;
        public DecThread(string name, int n)
        {
            thrd = new Thread(this.run);
            number = n;
            thrd.Name = name;
            thrd.Start();
        }
        void run()
        {
            Console.WriteLine(thrd.Name + "正在等待 the mutex");
            //申請
            shareRes.mutex.WaitOne();
            Console.WriteLine(thrd.Name + "申請到 the mutex");
            do
            {
                Thread.Sleep(1000);
                shareRes.count--;
                Console.WriteLine("In " + thrd.Name + "ShareRes.count is " + shareRes.count);
                number--;
            } while (number > 0);
            Console.WriteLine(thrd.Name + "釋放 the nmutex");
            //  釋放
            shareRes.mutex.ReleaseMutex();
        }
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            IncThread mthrd1 = new IncThread("IncThread thread ", 5);
            DecThread mthrd2 = new DecThread("DecThread thread ", 5);
            mthrd1.thrd.Join();
            mthrd2.thrd.Join();
        }
    }
}

 小結:

  悲觀鎖:查詢加鎖 【select ...... for update】

  樂觀鎖:修改加鎖 【版本號控制】

  排它鎖:事務A可以查詢、修改,直到事務A釋放為止才可以執行下一個事務

  共享鎖:事務A可以查詢、修改,同時事務B也可以查詢但不能修改

  互斥鎖:同一資源同一時間只能被一個線程訪問


免責聲明!

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



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