1、什么是鎖
鎖是為了解決多線程或者多進程資源競爭的問題。
同一進程的多個線程資源競爭可以用lock解決。
lock 關鍵字可確保當一個線程位於代碼的臨界區時,另一個線程不會進入該臨界區。 如果其他線程嘗試進入鎖定的代碼,則它將一直等待(即被阻止),直到該對象被釋放。
class Test
{
//定義一個私有成員變量,用於Lock
private static object lockobj = new object();
void DoSomething()
{
lock (lockobj)
{
//需要鎖定的代碼塊
}
}
}
多進程之間解決資源競爭問題我們則需要引入分布式鎖。通過一個協調者來解決,通常的解決辦法是通過redis來解決,這里不展開redis分布式鎖的討論。 接下來我們來聊聊如何自己實現一個分布式鎖(不依賴於redis)。
2、分布式鎖是個什么鬼
分布式鎖是分布式、微服務中一個必然要討論的話題。他為的是解決多進程多線程資源競爭的問題。

下面我們以訂單系統下單扣減庫存為例聊一聊扣減庫存的問題。
三個客戶KA、KB、KC同時下單購買物品P1,請求通過負載均衡器分發到訂單服務A、訂單服務B、訂單服務C。這個時候三個服務同時要對數據庫中的P1物品判斷庫存是否充足。假設庫存剩余10個,KA需要購買6個、KB需要購買6個、KC需要購買6個。
正常情況下服務A、B、C都查詢了庫存大於購買的數量,那么三個服務都判斷可以下單。此時我們可以看到,她們都進行下單明顯剩余庫存不足18個,那么就會出現超賣的問題。那我們怎么辦。我們第一時間會想到鎖,不過在分布式環境下程序自帶的Lock已經不能解決我們的問題。
消息隊列也可以解決這個問題,不過這里我們不討論,我們要討論的是用鎖來解決。
這個時候我們需要一個協調者來協調三個服務同時只能有一個請求進入下單代碼塊。原理同本地鎖一樣(當一個線程位於代碼的臨界區時,另一個線程不會進入該臨界區。 如果其他線程嘗試進入鎖定的代碼,則它將一直等待(即被阻止),直到該對象被釋放)。另外我們還需要注意的是,如果鎖的擁有者出現問題,不能及時釋放鎖。那么就會導致其他服務一直等待。那么就會出現死鎖的問題,因此我們也必須一如超時機制。在我們預設的處理時間內不能釋放鎖則需要協調者自動釋放鎖。防止出現死鎖。
轉載
.netcore 微服務快速開發框架 Anno&Viper -分布式鎖是個什么鬼
下面我們來看看微服務框架Anno是如何實現一個分布式鎖。
如果對Anno微服務框架不了解可以看這里《【開源】.net微服務開發引擎Anno開源啦》
2、實現一個分布式鎖
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleTest
{
using Anno.Const;
using Anno.EngineData;
using Anno.Loader;
using Anno.Rpc.Client;
using Anno.Rpc.Server;
using Autofac;
public class DLockTest
{
public void Handle()
{
Init();
To:
List<Task> ts = new List<Task>();
Console.WriteLine("請輸入線程數:");
int.TryParse(Console.ReadLine(), out int n);
for (int i = 0; i < n; i++)
{
var task = Task.Factory.StartNew(() => { DLTest1("Anno"); });
ts.Add(task);
//var taskXX = Task.Factory.StartNew(() => { DLTest1("Viper"); });
//ts.Add(taskXX);
//var taskJJ = Task.Factory.StartNew(() => { DLTest1("Key001"); });
//ts.Add(taskJJ);
}
Task.WaitAll(ts.ToArray());
goto To;
}
private void DLTest1(string lk = "duyanming")
{
try
{
Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss:ffff} {System.Threading.Thread.CurrentThread.ManagedThreadId} DLTest1拉取鎖({lk})");
using (DLock dLock = new DLock(lk, 10000))
{
Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss:ffff} {System.Threading.Thread.CurrentThread.ManagedThreadId} DLTest1進入鎖({lk})");
System.Threading.Thread.Sleep(50);
}
Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss:ffff} {System.Threading.Thread.CurrentThread.ManagedThreadId} DLTest1離開鎖({lk})");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
void Init()
{
//SettingService.AppName = "DLockTest";
//SettingService.Local.IpAddress = "127.0.0.1";
//SettingService.Local.Port = 6660;
IocLoader.GetAutoFacContainerBuilder().RegisterType(typeof(RpcConnectorImpl)).As(typeof(IRpcConnector)).SingleInstance();
IocLoader.Build();
DefaultConfigManager.SetDefaultConnectionPool(100, Environment.ProcessorCount * 2, 50);
DefaultConfigManager.SetDefaultConfiguration("DLockTest", "127.0.0.1", 6660, false);
}
}
}
GitHub地址:https://github.com/duyanming/Anno.Core/blob/master/test/ConsoleTest/DLockTest.cs

不同類型的鎖可以同時進入相互不影響
var task = Task.Factory.StartNew(() => { DLTest1("Anno"); });
ts.Add(task);
var taskXX = Task.Factory.StartNew(() => { DLTest1("Viper"); });
ts.Add(taskXX);
var taskJJ = Task.Factory.StartNew(() => { DLTest1("Key001"); });
ts.Add(taskJJ);

上圖我們開了12個進程同時進入DLTest1 方法,
using (DLock dLock = new DLock(lk, 10000))設置超時時間10秒。
關鍵代碼:
private void DLTest1(string lk = "duyanming")
{
try
{
Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss:ffff} {System.Threading.Thread.CurrentThread.ManagedThreadId} DLTest1拉取鎖({lk})");
using (DLock dLock = new DLock(lk, 10000))
{
Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss:ffff} {System.Threading.Thread.CurrentThread.ManagedThreadId} DLTest1進入鎖({lk})");
System.Threading.Thread.Sleep(50);
}
Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss:ffff} {System.Threading.Thread.CurrentThread.ManagedThreadId} DLTest1離開鎖({lk})");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
所有源碼都可以在 Anno中找到。
Anno核心源碼:https://github.com/duyanming/Anno.Core
Viper示例項目:https://github.com/duyanming/Viper
體驗地址:http://140.143.207.244/Home/Login
QQ交流群:478399354

