其實說多線程修改數(shù)據(jù)也不合適,畢竟redis服務(wù)端是單線程的,所有命令串行執(zhí)行,只是在客戶端并發(fā)發(fā)送命令的時候,導(dǎo)致串行的命令一些排列問題和網(wǎng)絡(luò)時間差等造成數(shù)據(jù)不一致。本文雖然是數(shù)字的加減,但是為了說明鎖的情況,故意不是用原子命令incr。
讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價值的長期合作伙伴,公司提供的服務(wù)項目有:域名注冊、網(wǎng)站空間、營銷軟件、網(wǎng)站建設(shè)、碾子山網(wǎng)站維護、網(wǎng)站推廣。
先配上一個簡易的RedisHelper,一個set值,一個get值,一個設(shè)置并發(fā)鎖,以便在我后面的操作中,你能清楚我究竟做了什么。
public class RedisHelper { public RedisClient client = new RedisClient("127.0.0.1", 6379); public void Set(string key, T val) { client.Set(key, val); } public T Get (string key) { var result = client.Get (key); return result; } public IDisposable Acquire(string key) { return client.AcquireLock(key); } }
下面看一下并發(fā)代碼,我只new了兩個Thread。兩個線程同時想訪問同一個key,分別訪問五萬次,在并發(fā)條件下,我們很難保證數(shù)據(jù)的準(zhǔn)確性,請比較輸出結(jié)果。
static void Main(string[] args) { RedisHelper rds = new RedisHelper(); rds.Set("mykey1", 0); Thread myThread1 = new Thread(AddVal); Thread myThread2 = new Thread(AddVal); myThread1.Start(); myThread2.Start(); Console.WriteLine("等待兩個線程結(jié)束"); Console.ReadKey(); } public static void AddVal() { RedisHelper rds = new RedisHelper(); for (int i = 0; i < 50000; i++) { int result = rds.Get ("mykey1"); rds.Set ("mykey1", result + 1); } Console.WriteLine("線程結(jié)束,輸出" + rds.Get ("mykey1")); }
是的,和我們單線程,跑兩個50000,會輸出100000?,F(xiàn)在是兩個并發(fā)線程同時跑在由于并發(fā)造成的數(shù)據(jù)結(jié)果往往不是我們想要的。那么如何解決這個問題呢,Redis已經(jīng)為我們準(zhǔn)備好了!
你可以看到我RedisHelper中有個方法是 public IDisposable Acquire(string key)。 也可以看到他返回的是IDisposable,證明我們需要手動釋放資源。
方法內(nèi)部的 AcquireLock正是關(guān)鍵之處,它像redis中索取一把鎖頭,被鎖住的資源,只能被單個線程訪問,不會被兩個線程同時get或者set,這兩個線程一定是交替著進行的,當(dāng)然這里的交替并不是指你一次我一次,也可能是你多次,我一次,下面看代碼。
static void Main(string[] args) { RedisHelper rds = new RedisHelper(); rds.Set("mykey1", 0); Thread myThread1 = new Thread(AddVal); Thread myThread2 = new Thread(AddVal); myThread1.Start(); myThread2.Start(); Console.WriteLine("等待兩個線程結(jié)束"); Console.ReadKey(); } public static void AddVal() { RedisHelper rds = new RedisHelper(); for (int i = 0; i < 50000; i++) { using (rds.Acquire("lock")) { int result = rds.Get ("mykey1"); rds.Set ("mykey1", result + 1); } } Console.WriteLine("線程結(jié)束,輸出" + rds.Get ("mykey1")); }
可以看到我使用了using,調(diào)用我的Acquire方法獲取鎖。
輸出結(jié)果最后是100000,正是我們要的正確結(jié)果。前面的8W+是因為兩個線程之一先執(zhí)行結(jié)束了。
還有,在正式使用的過程中,建議給我們的鎖,使用后刪除掉,并加上一個過期時間,使用expire。
以免程序執(zhí)行期間意外退出,導(dǎo)致鎖一直存在,今后可能無法更新或者獲取此被鎖住的數(shù)據(jù)。
你也可以嘗試一下不設(shè)置expire,在程序剛開始執(zhí)行時,關(guān)閉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鎖的簡單應(yīng)用介紹的詳細內(nèi)容,更多請關(guān)注創(chuàng)新互聯(lián)其它相關(guān)文章!