Redis鎖的簡單應用介紹

Redis鎖的簡單應用介紹

其實說多線程修改數據也不合適,畢竟redis服務端是單線程的,所有命令串行執行,只是在客戶端并發發送命令的時候,導致串行的命令一些排列問題和網絡時間差等造成數據不一致。本文雖然是數字的加減,但是為了說明鎖的情況,故意不是用原子命令incr。(推薦:redis視頻教程

先配上一個簡易的redisHelper,一個set值,一個get值,一個設置并發鎖,以便在我后面的操作中,你能清楚我究竟做了什么。

public?class?RedisHelper ????{ ????????public?RedisClient?client?=?new?RedisClient("127.0.0.1",?6379); ????????public?void?Set<t>(string?key,?T?val) ????????{ ????????????client.Set(key,?val); ????????} ????????public?T?Get<t>(string?key) ????????{ ????????????var?result?=?client.Get<t>(key); ????????????return?result; ????????} ????????public?IDisposable?Acquire(string?key) ????????{ ???????????return??client.AcquireLock(key); ????????} ????}</t></t></t>

下面看一下并發代碼,我只new了兩個Thread。兩個線程同時想訪問同一個key,分別訪問五萬次,在并發條件下,我們很難保證數據的準確性,請比較輸出結果。

static?void?Main(string[]?args) ????????{ ????????????RedisHelper?rds?=?new?RedisHelper(); ????????????rds.Set<int>("mykey1",?0); ????????????Thread?myThread1?=?new?Thread(AddVal); ????????????Thread?myThread2?=?new?Thread(AddVal); ????????????myThread1.Start(); ????????????myThread2.Start(); ????????????Console.WriteLine("等待兩個線程結束"); ????????????Console.ReadKey(); ????????}  ????????public?static?void?AddVal() ????????{ ????????????RedisHelper?rds?=?new?RedisHelper(); ????????????for?(int?i?=?0;?i?("mykey1"); ????????????????????rds.Set<int>("mykey1",?result?+?1); ???????????????? ????????????} ????????????Console.WriteLine("線程結束,輸出"?+?rds.Get<int>("mykey1")); ????????}</int></int></int>

Redis鎖的簡單應用介紹

是的,和我們單線程,跑兩個50000,會輸出100000。現在是兩個并發線程同時跑在由于并發造成的數據結果往往不是我們想要的。那么如何解決這個問題呢,Redis已經為我們準備好了!

你可以看到我RedisHelper中有個方法是?public IDisposable Acquire(string key)。??也可以看到他返回的是IDisposable,證明我們需要手動釋放資源。

方法內部的?AcquireLock正是關鍵之處,它像redis中索取一把鎖頭,被鎖住的資源,只能被單個線程訪問,不會被兩個線程同時get或者set,這兩個線程一定是交替著進行的,當然這里的交替并不是指你一次我一次,也可能是你多次,我一次,下面看代碼。

static?void?Main(string[]?args) ????????{ ????????????RedisHelper?rds?=?new?RedisHelper(); ????????????rds.Set<int>("mykey1",?0); ????????????Thread?myThread1?=?new?Thread(AddVal); ????????????Thread?myThread2?=?new?Thread(AddVal); ????????????myThread1.Start(); ????????????myThread2.Start(); ????????????Console.WriteLine("等待兩個線程結束"); ????????????Console.ReadKey(); ????????}  ????????public?static?void?AddVal() ????????{ ????????????RedisHelper?rds?=?new?RedisHelper(); ????????????for?(int?i?=?0;?i?("mykey1"); ????????????????????rds.Set<int>("mykey1",?result?+?1); ????????????????} ????????????} ????????????Console.WriteLine("線程結束,輸出"?+?rds.Get<int>("mykey1")); ????????}</int></int></int>

可以看到我使用了using,調用我的Acquire方法獲取鎖。

Redis鎖的簡單應用介紹

輸出結果最后是100000,正是我們要的正確結果。前面的8W+是因為兩個線程之一先執行結束了。

還有,在正式使用的過程中,建議給我們的鎖,使用后刪除掉,并加上一個過期時間,使用expire。

以免程序執行期間意外退出,導致鎖一直存在,今后可能無法更新或者獲取此被鎖住的數據。

你也可以嘗試一下不設置expire,在程序剛開始執行時,關閉console,重新運行程序,并且在redis-cli的操作控制臺,get你鎖住的值,將會永遠獲取不到。

所有連接此redis實例的機器,同一時刻,只能有一個獲取指定name的鎖.

下面是StackExchange.Redis的寫法

var?info?=?"name-"+Environment.MachineName; ????????????//如果5秒不釋放鎖?自動釋放。避免死鎖 ????????????if?(db.LockTake("name",?info,?TimeSpan.FromSeconds(5))) ????????????{ ????????????????try ????????????????{ ??????????????????? ????????????????} ????????????????catch?(Exception?ex) ????????????????{ ???????????????????? ????????????????} ????????????????finally ????????????????{ ??????????????????? ????????????????????db.LockRelease("name",?token); ????????????????} ????????????}

更多redis知識請關注redis視頻教程欄目。

? 版權聲明
THE END
喜歡就支持一下吧
點贊8 分享