使用 ADO.NET 和 Redis 来实现分布式锁是一个常见的做法,但需要注意的是,ADO.NET 本身并不直接支持分布式锁的实现。ADO.NET 主要用于数据库操作,而 Redis 则是一个内存中的数据结构存储系统,它提供了像分布式锁这样的高级功能。
下面是一个使用 C# 和 StackExchange.Redis 客户端库来实现分布式锁的示例:
首先,确保你已经安装了 StackExchange.Redis NuGet包,它提供了与 Redis 交互的 .NET 客户端。
csharp代码
| using StackExchange.Redis; | |
| using System; | |
| using System.Threading; | |
| public class RedisDistributedLock | |
| { | |
| private readonly ConnectionMultiplexer _redis; | |
| private readonly IDatabase _db; | |
| private readonly string _lockKey; | |
| private static readonly TimeSpan _lockTimeout = TimeSpan.FromSeconds(30); // 锁的超时时间 | |
| public RedisDistributedLock(string connectionString, string lockKey) | |
| { | |
| _redis = ConnectionMultiplexer.Connect(connectionString); | |
| _db = _redis.GetDatabase(); | |
| _lockKey = lockKey; | |
| } | |
| public bool TryAcquireLock(out RedisValue token) | |
| { | |
| // 使用 Redis 的 SET 命令尝试获取锁,NX 选项表示仅当 key 不存在时才设置,PX 选项设置锁的过期时间 | |
| var lockAcquired = _db.StringSet(_lockKey, Guid.NewGuid().ToString(), expiry: _lockTimeout, when: When.NotExists, flags: CommandFlags.PreferMaster); | |
| token = lockAcquired ? (RedisValue?)_db.StringGet(_lockKey) : default; | |
| return lockAcquired; | |
| } | |
| public bool ReleaseLock(RedisValue token) | |
| { | |
| // 使用 Redis 的脚本功能确保锁的释放是原子的,只有持有锁的客户端才能释放它 | |
| var script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then " + | |
| "return redis.call(\"del\",KEYS[1]) " + | |
| "else " + | |
| "return 0 " + | |
| "end"; | |
| var preparedScript = LuaScript.Prepare(script); | |
| var result = _db.ScriptEvaluate(preparedScript, new RedisKey[] { _lockKey }, new RedisValue[] { token }); | |
| return (int)result == 1; | |
| } | |
| } | |
| // 使用示例 | |
| class Program | |
| { | |
| static void Main(string[] args) | |
| { | |
| var lockKey = "my-distributed-lock"; | |
| var redisLock = new RedisDistributedLock("localhost", lockKey); | |
| if (redisLock.TryAcquireLock(out var token)) | |
| { | |
| try | |
| { | |
| // 在这里执行需要同步的代码 | |
| Console.WriteLine("Acquired lock. Performing task..."); | |
| Thread.Sleep(5000); // 模拟任务执行时间 | |
| } | |
| finally | |
| { | |
| // 无论任务是否成功,都要确保锁被释放 | |
| if (redisLock.ReleaseLock(token)) | |
| { | |
| Console.WriteLine("Lock released."); | |
| } | |
| else | |
| { | |
| Console.WriteLine("Failed to release lock."); | |
| } | |
| } | |
| } | |
| else | |
| { | |
| Console.WriteLine("Failed to acquire lock."); | |
| } | |
| } | |
| } |
这个示例代码展示了如何使用 StackExchange.Redis 客户端库来实现一个简单的分布式锁。TryAcquireLock 方法尝试获取锁,并在成功时返回一个 token,这个 token 稍后用于释放锁。ReleaseLock 方法使用 Redis 的 Lua 脚本功能来确保只有锁的持有者才能释放它。
请注意,这个示例是一个简化的版本,用于演示基本概念。在生产环境中,你可能需要考虑更多的细节,比如锁续期(避免任务执行时间超过锁的过期时间)、锁的竞争条件、锁的公平性、Redis 实例的可用性等。此外,如果你正在使用 .NET Core 或 .NET 5/6/7 等较新版本,你可能会使用 Microsoft.Extensions.Caching.StackExchangeRedis 包来与 Redis 交互,但这并不影响分布式锁的实现逻辑。