欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C#實現(xiàn)Redis的分布式鎖

 更新時間:2021年08月04日 09:50:04   作者:閑僧  
我們在開發(fā)很多業(yè)務場景會使用到鎖,例如庫存控制,抽獎等。分布式與單機情況下最大的不同在于其不是多線程而是多進程。本文就來介紹一下,感興趣的可以了解一下

Redis實現(xiàn)分布式鎖(悲觀鎖/樂觀鎖)

對鎖的概念和應用場景在此就不闡述了,網(wǎng)上搜索有很多解釋,只是我搜索到的使用C#利用Redis的SetNX命令實現(xiàn)的鎖雖然能用,但是都不太適合我需要的場景。

Redis有三個最基本屬性來保證分布式鎖的有效實現(xiàn):

  • 安全性: 互斥,在任何時候,只有一個客戶端能持有鎖。
  • 活躍性A:沒有死鎖,即使客戶端在持有鎖的時候崩潰,最后也會有其他客戶端能獲得鎖,超時機制。
  • 活躍性B:故障容忍,只有大多數(shù)Redis節(jié)點時存活的,客戶端仍可以獲得鎖和釋放鎖。

基于ServiceStack.Redis寫了一個幫助類

Redis連接池

        public static PooledRedisClientManager RedisClientPool = CreateManager();

        private static PooledRedisClientManager CreateManager()
        {
            var redisHosts = System.Configuration.ConfigurationManager.AppSettings["redisHosts"];
            if (string.IsNullOrEmpty(redisHosts))
            {
                throw new Exception("AppSetting redisHosts no found");
            }

            string[] redisHostarr = redisHosts.Split(new string[] { ",", "," }, StringSplitOptions.RemoveEmptyEntries);

            return new PooledRedisClientManager(redisHostarr, redisHostarr, new RedisClientManagerConfig
            {
                MaxWritePoolSize = 1000,
                MaxReadPoolSize = 1000,
                AutoStart = true,
                DefaultDb = 0
            });
        }

使用Redis的SetNX命令實現(xiàn)加鎖,

        /// <summary>
        /// 加鎖
        /// </summary>
        /// <param name="key">鎖key</param>
        /// <param name="selfMark">自己標記</param>
        /// <param name="lockExpirySeconds">鎖自動過期時間[默認10](s)</param>
        /// <param name="waitLockMilliseconds">等待鎖時間(ms)</param>
        /// <returns></returns>
        public static bool Lock(string key, out string selfMark, int lockExpirySeconds = 10, long waitLockMilliseconds = long.MaxValue)
        {
            DateTime begin = DateTime.Now;
            selfMark = Guid.NewGuid().ToString("N");//自己標記,釋放鎖時會用到,自己加的鎖除非過期否則只能自己打開
            using (RedisClient redisClient = (RedisClient)RedisClientPool.GetClient())
            {
                string lockKey = "Lock:" + key;
                while (true)
                {
                    string script = string.Format("if redis.call('SETNX', KEYS[1], ARGV[1]) == 1 then redis.call('PEXPIRE',KEYS[1],{0}) return 1 else return 0 end", lockExpirySeconds * 1000);
                    //循環(huán)獲取取鎖
                    if (redisClient.ExecLuaAsInt(script, new[] { lockKey }, new[] { selfMark }) == 1)
                    {
                        return true;
                    }

                    //不等待鎖則返回
                    if (waitLockMilliseconds == 0)
                    {
                        break;
                    }

                    //超過等待時間,則不再等待
                    if ((DateTime.Now - begin).TotalMilliseconds >= waitLockMilliseconds)
                    {
                        break;
                    }
                    Thread.Sleep(100);
                }

                return false;
            }
        }

因為ServiceStack.Redis提供的SetNX方法,并沒有提供設置過期時間的方法,對于加鎖業(yè)務又不能分開執(zhí)行(如果加鎖成功設置過期時間失敗導致的永久死鎖問題),所以就使用腳本實現(xiàn),解決了異常情況死鎖問題.

  • 參數(shù)key:鎖的key
  • 參數(shù)selfMark:在設置鎖的時候會產(chǎn)生一個自己的標識,在釋放鎖的時候會用到,所謂解鈴還須系鈴人。防止鎖被誤釋放,導致鎖無效.
  • 參數(shù)lockExpirySeconds:鎖的默認過期時間,防止被永久死鎖.
  • 參數(shù)waitLockMilliseconds:循環(huán)獲取鎖的等待時間.

如果設置為0,為樂觀鎖機制,獲取不到鎖,直接返回未獲取到鎖.
默認值為long最大值,為悲觀鎖機制,約等于很多很多天,可以理解為一直等待.

釋放鎖

        /// <summary>
        /// 釋放鎖
        /// </summary>
        /// <param name="key">鎖key</param>
        /// <param name="selfMark">自己標記</param>
        public void UnLock(string key, string selfMark)
        {
            using (RedisClient redisClient = (RedisClient)RedisClientPool.GetClient())
            {
                string lockKey = "Lock:" + key;
                var script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
                redisClient.ExecLuaAsString(script, new[] { lockKey }, new[] { selfMark });
            }
        }

參數(shù)key:鎖的key
參數(shù)selfMark:在設置鎖的時候返回的自己標識,用來解鎖自己加的鎖(此值不能隨意傳,必須是加鎖時返回的值)

調用方式

悲觀鎖方式

            int num = 10;
            string lockkey = "xianseng";

            //悲觀鎖開啟20個人同時拿寶貝
            for (int i = 0; i < 20; i++)
            {
                Task.Run(() =>
                {
                    string selfmark = "";
                    try
                    {
                        if (PublicLockHelper.Lock(lockkey, out selfmark))
                        {
                            if (num > 0)
                            {
                                num--;
                                Console.WriteLine($"我拿到了寶貝:寶貝剩余{num}個\t\t{selfmark}");
                            }
                            else
                            {
                                Console.WriteLine("寶貝已經(jīng)沒有了");
                            }
                            Thread.Sleep(100);
                        }
                    }
                    finally
                    {
                        PublicLockHelper.UnLock(lockkey, selfmark);
                    }
                });
            }

樂觀鎖方式

            int num = 10;
            string lockkey = "xianseng";

            //樂觀鎖開啟10個線程,每個線程拿5次
            for (int i = 0; i < 10; i++)
            {
                Task.Run(() =>
                {
                    for (int j = 0; j < 5; j++)
                    {
                        string selfmark = "";
                        try
                        {
                            if (PublicLockHelper.Lock(lockkey, out selfmark, 10, 0))
                            {
                                if (num > 0)
                                {
                                    num--;
                                    Console.WriteLine($"我拿到了寶貝:寶貝剩余{num}個\t\t{selfmark}");
                                }
                                else
                                {
                                    Console.WriteLine("寶貝已經(jīng)沒有了");
                                }

                                Thread.Sleep(1000);
                            }
                            else
                            {
                                Console.WriteLine("沒有拿到,不想等了");
                            }
                        }
                        finally
                        {
                            PublicLockHelper.UnLock(lockkey, selfmark);
                        }
                    }
                });
            }

單機只能用多線模擬使用分布式鎖了

此鎖已經(jīng)可以滿足大多數(shù)場景了,若有不妥,還請多多指出,以免誤別人!

(次方案不支持Redis集群,Redis集群不能調用腳本執(zhí)行)

到此這篇關于C#實現(xiàn)Redis的分布式鎖的文章就介紹到這了,更多相關C# Redis分布式鎖內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • C#中async和await的深入分析

    C#中async和await的深入分析

    Async/Await是C# 5引入的關鍵字,用以提高用戶界面響應能力和對Web資源的訪問能力,同時它使異步代碼的編寫變得更加容易,下面這篇文章主要給大家介紹了關于C#中async和await的相關資料,需要的朋友可以參考下
    2022-11-11
  • C#類中的屬性使用總結(詳解類的屬性)

    C#類中的屬性使用總結(詳解類的屬性)

    屬性是一種類的成員,它的實現(xiàn)類似函數(shù),訪問類似字段。它的作用是提供一種靈活和安全的機制來訪問,修改私有字段。所以屬性必須依賴于字段
    2014-03-03
  • Winform控件優(yōu)化之圓角按鈕2

    Winform控件優(yōu)化之圓角按鈕2

    這篇文章主要介紹了Winform控件優(yōu)化之圓角按鈕2,文章通過圍繞主題展開詳細的內容介紹,具有一定的參考價值,感興趣的小伙伴可以參考一下
    2022-08-08
  • C#中Lambda表達式的用法

    C#中Lambda表達式的用法

    這篇文章介紹了C#中Lambda表達式的用法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-05-05
  • C#獲取文件夾及文件的大小與占用空間的方法

    C#獲取文件夾及文件的大小與占用空間的方法

    這篇文章主要介紹了C#獲取文件夾及文件的大小與占用空間的方法,需要的朋友可以參考下
    2014-07-07
  • C#操作XML文件步驟

    C#操作XML文件步驟

    在本篇文章里小編給大家分享了關于C#操作XML文件步驟教學內容,有興趣的朋友們可以學習下。
    2019-01-01
  • C#連接Mysql實現(xiàn)增刪改查的操作

    C#連接Mysql實現(xiàn)增刪改查的操作

    在IT行業(yè)中,數(shù)據(jù)庫連接是應用程序開發(fā)中的重要環(huán)節(jié),尤其是在使用C#進行Windows或者Web應用開發(fā)時,經(jīng)常需要與各種數(shù)據(jù)庫進行交互,其中就包括廣泛使用的MySQL,本篇將詳細講解如何使用C#語言來連接MySQL數(shù)據(jù)庫,以實現(xiàn)數(shù)據(jù)的讀取、寫入和其他操作
    2024-09-09
  • C#語法糖(Csharp Syntactic sugar)大匯總

    C#語法糖(Csharp Syntactic sugar)大匯總

    首先需要聲明的是“語法糖”這個詞絕非貶義詞,它可以給我?guī)矸奖?,是一種便捷的寫法,編譯器會幫我們做轉換;而且可以提高開發(fā)編碼的效率,在性能上也不會帶來損失。這讓java開發(fā)人員羨慕不已,呵呵。
    2010-06-06
  • 詳解C#的設計模式編程之抽象工廠模式的應用

    詳解C#的設計模式編程之抽象工廠模式的應用

    這篇文章主要介紹了C#的設計模式編程之抽象工廠模式的應用,注意區(qū)分一下簡單工廠模式、工廠方法模式和抽象工廠模式概念之間的區(qū)別,需要的朋友可以參考下
    2016-02-02
  • C# 添加、修改和刪除PDF書簽的實例代碼

    C# 添加、修改和刪除PDF書簽的實例代碼

    本篇文章主要介紹了C# 添加、修改和刪除PDF書簽的實例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07

最新評論