Redis鎖的簡單應用


本文版權歸博客園和作者本人吳雙共同所有 。轉載爬蟲請注明地址,博客園蝸牛 http://www.cnblogs.com/tdws/p/5712835.html

蝸牛Redis系列文章目錄http://www.cnblogs.com/tdws/tag/NoSql/

Redis Cluster http://www.cnblogs.com/tdws/p/7710545.html

其實說多線程修改數據也不合適,畢竟redis服務端是單線程的,所有命令串行執行,只是在客戶端並發發送命令的時候,導致串行的命令一些排列問題和網絡時間差等造成數據不一致。本文雖然是數字的加減,但是為了說明鎖的情況,故意不是用原子命令incr。也並非分布式鎖的正確實現,沒有考慮一些重入性等,稍后會整理一篇分布式鎖的實踐。

Redis分布式鎖 http://www.cnblogs.com/tdws/p/5808528.html

ZK+curator 分布式鎖 http://www.cnblogs.com/tdws/p/5874686.html

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

 1  public class RedisHelper
 2     {
 3         public RedisClient client = new RedisClient("127.0.0.1", 6379);
 4         public void Set<T>(string key, T val)
 5         {
 6             client.Set(key, val);
 7         }
 8         public T Get<T>(string key)
 9         {
10             var result = client.Get<T>(key);
11             return result;
12         }
13         public IDisposable Acquire(string key)
14         {
15            return  client.AcquireLock(key);
16         }
17     }
View Code

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

 1 static void Main(string[] args)
 2         {
 3             RedisHelper rds = new RedisHelper();
 4             rds.Set<int>("mykey1", 0);
 5             Thread myThread1 = new Thread(AddVal);
 6             Thread myThread2 = new Thread(AddVal);
 7             myThread1.Start();
 8             myThread2.Start();
 9             Console.WriteLine("等待兩個線程結束");
10             Console.ReadKey();
11         }
12 
13         public static void AddVal()
14         {
15             RedisHelper rds = new RedisHelper();
16             for (int i = 0; i < 50000; i++)
17             {
18                 
19                     int result = rds.Get<int>("mykey1");
20                     rds.Set<int>("mykey1", result + 1);
21                 
22             }
23             Console.WriteLine("線程結束,輸出" + rds.Get<int>("mykey1"));
24         }
View Code

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

你可以看到我RedisHelper中有個方法是 public IDisposable Acquire(string key)。  也可以看到他返回的是IDisposable,證明我們需要手動釋放資源。方法內部的 AcquireLock正是關鍵之處,它像redis中索取一把鎖頭,被鎖住的資源,只能被單個線程訪問,不會被兩個線程同時get或者set,這兩個線程一定是交替着進行的,當然這里的交替並不是指你一次我一次,也可能是你多次,我一次,下面看代碼。

 1  static void Main(string[] args)
 2         {
 3             RedisHelper rds = new RedisHelper();
 4             rds.Set<int>("mykey1", 0);
 5             Thread myThread1 = new Thread(AddVal);
 6             Thread myThread2 = new Thread(AddVal);
 7             myThread1.Start();
 8             myThread2.Start();
 9             Console.WriteLine("等待兩個線程結束");
10             Console.ReadKey();
11         }
12 
13         public static void AddVal()
14         {
15             RedisHelper rds = new RedisHelper();
16             for (int i = 0; i < 50000; i++)
17             {
18                 using (rds.Acquire("lock"))
19                 {
20                     int result = rds.Get<int>("mykey1");
21                     rds.Set<int>("mykey1", result + 1);
22                 }
23             }
24             Console.WriteLine("線程結束,輸出" + rds.Get<int>("mykey1"));
25         }
View Code

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

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

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

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

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

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

下面是StackExchange.Redis的寫法

 1            var info = "name-"+Environment.MachineName;
 2             //如果5秒不釋放鎖 自動釋放。避免死鎖
 3             if (db.LockTake("name", info, TimeSpan.FromSeconds(5)))
 4             {
 5                 try
 6                 {
 7                    
 8                 }
 9                 catch (Exception ex)
10                 {
11                     
12                 }
13                 finally
14                 {
15                    
16                     db.LockRelease("name", token);
17                 }
18             }

 


免責聲明!

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



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