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

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

 更新時(shí)間:2021年08月04日 09:50:04   作者:閑僧  
我們?cè)陂_(kāi)發(fā)很多業(yè)務(wù)場(chǎng)景會(huì)使用到鎖,例如庫(kù)存控制,抽獎(jiǎng)等。分布式與單機(jī)情況下最大的不同在于其不是多線程而是多進(jìn)程。本文就來(lái)介紹一下,感興趣的可以了解一下

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

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

Redis有三個(gè)最基本屬性來(lái)保證分布式鎖的有效實(shí)現(xiàn):

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

基于ServiceStack.Redis寫了一個(gè)幫助類

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命令實(shí)現(xiàn)加鎖,

        /// <summary>
        /// 加鎖
        /// </summary>
        /// <param name="key">鎖key</param>
        /// <param name="selfMark">自己標(biāo)記</param>
        /// <param name="lockExpirySeconds">鎖自動(dòng)過(guò)期時(shí)間[默認(rèn)10](s)</param>
        /// <param name="waitLockMilliseconds">等待鎖時(shí)間(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");//自己標(biāo)記,釋放鎖時(shí)會(huì)用到,自己加的鎖除非過(guò)期否則只能自己打開(kāi)
            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;
                    }

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

                return false;
            }
        }

因?yàn)镾erviceStack.Redis提供的SetNX方法,并沒(méi)有提供設(shè)置過(guò)期時(shí)間的方法,對(duì)于加鎖業(yè)務(wù)又不能分開(kāi)執(zhí)行(如果加鎖成功設(shè)置過(guò)期時(shí)間失敗導(dǎo)致的永久死鎖問(wèn)題),所以就使用腳本實(shí)現(xiàn),解決了異常情況死鎖問(wèn)題.

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

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

釋放鎖

        /// <summary>
        /// 釋放鎖
        /// </summary>
        /// <param name="key">鎖key</param>
        /// <param name="selfMark">自己標(biāo)記</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:在設(shè)置鎖的時(shí)候返回的自己標(biāo)識(shí),用來(lái)解鎖自己加的鎖(此值不能隨意傳,必須是加鎖時(shí)返回的值)

調(diào)用方式

悲觀鎖方式

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

            //悲觀鎖開(kāi)啟20個(gè)人同時(shí)拿寶貝
            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}個(gè)\t\t{selfmark}");
                            }
                            else
                            {
                                Console.WriteLine("寶貝已經(jīng)沒(méi)有了");
                            }
                            Thread.Sleep(100);
                        }
                    }
                    finally
                    {
                        PublicLockHelper.UnLock(lockkey, selfmark);
                    }
                });
            }

樂(lè)觀鎖方式

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

            //樂(lè)觀鎖開(kāi)啟10個(gè)線程,每個(gè)線程拿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}個(gè)\t\t{selfmark}");
                                }
                                else
                                {
                                    Console.WriteLine("寶貝已經(jīng)沒(méi)有了");
                                }

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

單機(jī)只能用多線模擬使用分布式鎖了

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

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

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

相關(guān)文章

  • 基于C#實(shí)現(xiàn)Json轉(zhuǎn)Lua的操作指南

    基于C#實(shí)現(xiàn)Json轉(zhuǎn)Lua的操作指南

    JSON(JavaScript Object Notation)是一種輕量級(jí)的數(shù)據(jù)交換格式,它的語(yǔ)法基于 JavaScript 對(duì)象表示法,簡(jiǎn)單、易讀,同時(shí)被許多編程語(yǔ)言支持,本文給大家介紹了如何基于C#實(shí)現(xiàn)Json轉(zhuǎn)Lua的操作指南,需要的朋友可以參考下
    2024-12-12
  • C# XML與Json之間相互轉(zhuǎn)換實(shí)例詳解

    C# XML與Json之間相互轉(zhuǎn)換實(shí)例詳解

    這篇文章主要介紹了C# XML與Json之間相互轉(zhuǎn)換實(shí)例詳解,大家參考使用吧
    2013-11-11
  • 利用C#實(shí)現(xiàn)批量圖片格式轉(zhuǎn)換功能

    利用C#實(shí)現(xiàn)批量圖片格式轉(zhuǎn)換功能

    這篇文章主要為大家詳細(xì)介紹了如何利用C#實(shí)現(xiàn)批量圖片格式轉(zhuǎn)換功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2022-12-12
  • C# WORD操作實(shí)現(xiàn)代碼

    C# WORD操作實(shí)現(xiàn)代碼

    在當(dāng)前項(xiàng)目開(kāi)發(fā)過(guò)程中,客戶有根據(jù)數(shù)據(jù)庫(kù)數(shù)據(jù)生成WORD文檔的需求,在和同事溝通的過(guò)程中,找到了兩個(gè)解決方案
    2009-04-04
  • WPF實(shí)現(xiàn)曲線數(shù)據(jù)展示

    WPF實(shí)現(xiàn)曲線數(shù)據(jù)展示

    這篇文章將以動(dòng)數(shù)據(jù)分析為例為大家詳細(xì)介紹wpf實(shí)現(xiàn)曲線數(shù)據(jù)展示與函數(shù)曲線展示的方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以參考一下
    2024-12-12
  • 遍歷Hashtable 的幾種方法

    遍歷Hashtable 的幾種方法

    遍歷Hashtable 的幾種方法...
    2007-03-03
  • C#獲取ListView鼠標(biāo)下的Item實(shí)例

    C#獲取ListView鼠標(biāo)下的Item實(shí)例

    下面小編就為大家?guī)?lái)一篇C#獲取ListView鼠標(biāo)下的Item實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-01-01
  • C#數(shù)據(jù)結(jié)構(gòu)與算法揭秘二

    C#數(shù)據(jù)結(jié)構(gòu)與算法揭秘二

    上文對(duì)數(shù)據(jù)結(jié)構(gòu)與算法,有了一個(gè)簡(jiǎn)單的概述與介紹,這篇文章,我們介紹一中典型數(shù)據(jù)結(jié)構(gòu)——線性結(jié)構(gòu)
    2012-10-10
  • Windows下C#的GUI窗口程序中實(shí)現(xiàn)調(diào)用Google Map的實(shí)例

    Windows下C#的GUI窗口程序中實(shí)現(xiàn)調(diào)用Google Map的實(shí)例

    這篇文章主要介紹了Windows下C#的GUI窗口程序中實(shí)現(xiàn)調(diào)用Google Map的實(shí)例,如果只想調(diào)用瀏覽器打開(kāi)網(wǎng)頁(yè)的話可以看文章最后的方法,需要的朋友可以參考下
    2016-04-04
  • C#讀寫json文件操作的正確方法

    C#讀寫json文件操作的正確方法

    在現(xiàn)代開(kāi)發(fā)中JSON已經(jīng)成為了一種非常流行的數(shù)據(jù)格式,下面這篇文章主要給大家介紹了關(guān)于C#讀寫json文件操作的正確方法,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-07-07

最新評(píng)論