轉載爬蟲請註明地址,博客園蝸牛 http://www.cnblogs.com/tdws/p/5712835.html 先配上一個簡易的RedisHelper,一個set值,一個get值,一個設置併發鎖,以便在我後面的操作中,你能清楚我究竟做了什麼。 1 public class RedisHelpe ...
轉載爬蟲請註明地址,博客園蝸牛 http://www.cnblogs.com/tdws/p/5712835.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 Setnx(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 Setnx(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.Setnx("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,調用我的Setnx方法獲取鎖。
輸出結果最後是100000,正是我們要的正確結果。前面的8W+是因為兩個線程之一先執行結束了。
還有,在正式使用的過程中,建議給我們的鎖,使用後刪除掉,並加上一個過期時間,使用expire。
以免程式執行期間意外退出,導致鎖一直存在,今後可能無法更新或者獲取此被鎖住的數據。
你也可以嘗試一下不設置expire,在程式剛開始執行時,關閉console,重新運行程式,並且在redis-cli的操作控制台,get你鎖住的值,將會永遠獲取不到。