.net下 本地锁、redis分布式锁、zk分布式锁的实现
为什么要用锁?
大型站点在高并发的情况下,为了保持数据最终一致性就需要用到技术方案来支持。比如:分布式锁、分布式事务。有时候我们在为了保证某一个方法每次只能被一个调用者使用的时候,这时候我们也可以锁来实现。
基于本地缓存实现锁
为什么还要写基于本地缓存实现的锁呢,因为有些项目项目可能还是单机部署的,当随着业务量增长的时候就会变成多机部署,从单机到多机的切换过程中,我们也需要把原先业务相关的锁改成分布式锁,来保持数据的最终一致性。当然项目是使用ioc的那就更好了,切换注册时的实现类就完成了切换,非常方便。
实现思路:
用户需要用一个key和一个唯一的值(知道当前这个key的使用者是谁)来获取一个锁,获取到锁之后,执行完对应的操作然后释放掉。在释放锁的时候 4我们需要判断下当前这个锁的使用者对应的值与想要释放传递过来的值是不是相等,如果相等则可以释放,不相等则不释放。
实现代码:

public sealed class LocalLock : ILock { private static ConcurrentDictionary<string, object> _LockCache = new ConcurrentDictionary<string, object>(); private static ConcurrentDictionary<string, string> _LockUserCache = new ConcurrentDictionary<string, string>(); /// <summary> /// 获取一个锁(需要自己释放) /// </summary> /// <param name="key">锁的键</param> /// <param name="value">当前占用值</param> /// <param name="span">耗时时间</param> /// <returns>成功返回true</returns> public bool LockTake(string key, string value, TimeSpan span) { EnsureUtil.NotNullAndNotEmpty(key, "Lockkey"); EnsureUtil.NotNullAndNotEmpty(value, "Lockvalue"); var obj = _LockCache.GetValue(key, () => { return new object(); }); if (Monitor.TryEnter(obj, span)) { _LockUserCache[key] = value; return true; } return false; } /// <summary> /// 异步获取一个锁(需要自己释放) /// </summary> /// <param name="key">锁的键</param> /// <param name="value">当前占用值</param> /// <param name="span">耗时时间</param> /// <returns>成功返回true</returns> public Task<bool> LockTakeAsync(string key, string value, TimeSpan span) { return Task.FromResult(LockTake(key, value, span)); } /// <summary> /// 释放一个锁 /// </summary> /// <param name="key">锁的键</param> /// <param name="value">当前占用值</param> /// <returns>成功返回true</returns> public bool LockRelease(string key, string value) { EnsureUtil.NotNullAndNotEmpty(key, "Lockkey"); EnsureUtil.NotNullAndNotEmpty(value, "Lockvalue"); _LockCache.TryGetValue(key, out object obj); if (obj != null) { if (_LockUserCache[key] == value) { Monitor.Exit(obj); return true; } return false; } return true; } /// <summary> /// 异步释放一个锁 /// </summary> /// <param name="key">锁的键</param> /// <param name="value">当前占用值</param> /// <returns>成功返回true</returns> public Task<bool> LockReleaseAsync(string key, string value) { return Task.FromResult(LockRelease(key, value)); } /// <summary> /// 使用锁执行一个方法 /// </summary> /// <param name="key">锁的键</param> /// <param name="value">当前占用值</param> /// <param name="span">耗时时间</param> /// <param name="executeAction">要执行的方法</param> public void ExecuteWithLock(string key, string value, TimeSpan span, Action executeAction) { if (executeAction == null) return; if (LockTake(key, value, span)) { try { executeAction(); } finally { LockRelease(key, value); } } } /// <summary> /// 使用锁执行一个方法 /// </summary> /// <typeparam name="T">返回值类型</typeparam> /// <param name="key">锁的键</param> /// <param name="value">当前占用值</param> /// <param name="span">耗时时间</param> /// <param name="executeAction">要执行的方法</param> /// <param name="defaultValue">默认返回</param> /// <returns></returns> public T ExecuteWithLock<T>(string key, string value, TimeSpan span, Func<T> executeAction, T defaultValue = default(T)) { if (executeAction == null) return defaultValue; if (LockTake(key, value, span)) { try { return executeAction(); } finally { LockRelease(key, value); } } return defaultValue; } /// <summary> /// 使用锁执行一个异步方法 /// </summary> /// <param name="key">锁的键</param> /// <param name="value">当前占用值</param> /// <param name="span">耗时时间</param> /// <param name="executeAction">要执行的方法</param> public async Task ExecuteWithLockAsync(string key, string value, TimeSpan span, Func<Task> executeAction) { if (executeAction == null) return; if (await LockTakeAsync(key, value, span)) { try { await executeAction(); } catch { throw; } finally { LockRelease(key, value); } } } /// <summary> /// 使用锁执行一个异步方法 /// </summary> /// <typeparam name="T">返回值类型</typeparam> /// <param name="key">锁的键</param> /// <param name="value">当前占用值</param> /// <param name="span">耗时时间</param> /// <param name="executeAction">要执行的方法</param> /// <param name="defaultValue">默认返回</param> /// <returns></returns> public async Task<T> ExecuteWithLockAsync<T>(string key, string value, TimeSpan span, Func<Task<T>> executeAction, T defaultValue = default(T)) { if (executeAction == null) return defaultValue; if (await LockTakeAsync(key, value, span)) { try { return await executeAction(); } catch { throw; } finally { LockRelease(key, value); } } return defaultValue; } }

class Program { static void Main(string[] args) { ILock localLock = new LocalLock(); int excuteCount = 0; Parallel.For(0, 10000, i => { localLock.ExecuteWithLock("test", Guid.NewGuid().ToString(), TimeSpan.FromSeconds(5), () => { Console.WriteLine("获取锁成功"); Interlocked.Increment(ref excuteCount); }); }); Console.WriteLine("成功次数:" + excuteCount.ToString()); Console.WriteLine("执行完成"); Console.ReadLine(); } }
基于zk实现的分布式锁
实现思路:
在获取锁的时候在固定节点下创建一个自增的临时节点,然后获取节点列表,按照增量排序,假如当前创建的节点是排在第一个的,那就表明这个节点是得到了执行的权限,假如在它前面还有其它节点,那么就对它的上一个节点进行监听,等到上一个节点被删除了,那么该节点就得到了执行的权限了。
由于代码片段太多,待会再github自行查看实现过程。zk获取锁的速度比较慢,导致有几个可能是失败的。
基于redis的实现
实现思路:
利用redis的setnx(key, value):“set if not exits”,若该key-value不存在,则成功加入缓存并且返回1,否则返回0。在有效时间内如果设置成功则获取执行限权,没有那就获取权限失败。
对比下会发现redis的执行效率会比zk的快一点。
项目下载地址:https://github.com/ProjectSharing/Lock
作者:yjq
欢迎任何形式的转载,但请务必注明出处。
.netcore相关功能需要定制的可以找我。有丰富的并发处理,性能优化经验。单点登录,限流,熔断,读写分离等功能均可定制。也可以帮忙系统优化处理,系统诊断,请联系博主(备注添加原因)。微信:yjq425527169 QQ:425527169
本篇文章如有些许帮助请点击推荐让更多需要帮助的人可以看到,请支持原创,请大方打赏(右边点击打赏)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?