使用 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 交互,但这并不影响分布式锁的实现逻辑。