我試圖使用ServiceStack-Redis庫和described here提供的鎖定機制來實現DLM,但是我發現API似乎會呈現競爭條件,有時會授予鎖定多個客戶端。使用ServiceStack Redis進行分佈式鎖定的互斥體違規
BasicRedisClientManager mgr = new BasicRedisClientManager(redisConnStr);
using(var client = mgr.GetClient())
{
client.Remove("touchcount");
client.Increment("touchcount", 0);
}
Random rng = new Random();
Action<object> simulatedDistributedClientCode = (clientId) => {
using(var redisClient = mgr.GetClient())
{
using(var mylock = redisClient.AcquireLock("mutex", TimeSpan.FromSeconds(2)))
{
long touches = redisClient.Get<long>("touchcount");
Debug.WriteLine("client{0}: I acquired the lock! (touched: {1}x)", clientId, touches);
if(touches > 0) {
Debug.WriteLine("client{0}: Oh, but I see you've already been here. I'll release it.", clientId);
return;
}
int arbitraryDurationOfExecutingCode = rng.Next(100, 2500);
Thread.Sleep(arbitraryDurationOfExecutingCode); // do some work of arbitrary duration
redisClient.Increment("touchcount", 1);
}
Debug.WriteLine("client{0}: Okay, I released my lock, your turn now.", clientId);
}
};
Action<Task> exceptionWriter = (t) => {if(t.IsFaulted) Debug.WriteLine(t.Exception.InnerExceptions.First());};
int arbitraryDelayBetweenClients = rng.Next(5, 500);
var clientWorker1 = new Task(simulatedDistributedClientCode, 1);
var clientWorker2 = new Task(simulatedDistributedClientCode, 2);
clientWorker1.Start();
Thread.Sleep(arbitraryDelayBetweenClients);
clientWorker2.Start();
Task.WaitAll(
clientWorker1.ContinueWith(exceptionWriter),
clientWorker2.ContinueWith(exceptionWriter)
);
using(var client = mgr.GetClient())
{
var finaltouch = client.Get<long>("touchcount");
Console.WriteLine("Touched a total of {0}x.", finaltouch);
}
mgr.Dispose();
當運行上述代碼來模擬兩個客戶端試圖彼此的短連續內相同的操作,有三種可能的輸出。第一個是互斥工作正常並且客戶按正確順序進行的最佳情況。第二種情況是第二個客戶端超時等待獲取鎖定;也是可以接受的結果。然而,問題是,當arbitraryDurationOfExecutingCode
接近或超過獲取鎖定的超時時間時,很容易重現第二個客戶端在第一個客戶端發佈之前被授予鎖定的情形,產生如下輸出:
client1:我獲得了鎖定! (感動:0x)
client2:我獲得了鎖! (感動:0x)
client1:好的,我釋放了我的鎖,現在輪到你了。 client2:好的,我釋放了我的鎖,現在輪到你了。
共觸摸了2次。
我對API的理解及其文檔是獲取鎖時timeOut
說法,就是要這樣 - 對超時越來越鎖。如果我不得不猜測timeOut
的值足夠高,以致總是比我執行的代碼的持續時間長,以防止出現這種情況,這似乎很容易出錯。除了傳遞null來永遠等待鎖之外,是否有人有其他解決方法?我絕對不想那樣做,或者我知道我會找到來自墜毀工人的鬼鎖。
謝謝你,這證實了我所看到的,那'timeOut'參數被用作鎖定獲取超時和鎖定期限。 – Dusty 2014-10-07 16:44:50