环境:
- win10
- sqlserver 2014
- vs2019
参照: 基于数据库实现的分布式锁
实现过程:
1、在数据库中新建一张锁表
create table db_lock(
id int identity(1,1),
lock_str varchar(200) unique not null,
lock_time datetime default(GetDate()),
lock_userid int not null
)
2、c#代码中获取锁和释放锁
代码中的
Common.GetIDb()
表示的是获取数据库的操作对象,具体可以参照:https://blog.csdn.net/u010476739/article/details/54882950
public class DBLock
{
/// <summary>
/// 获取分布式锁
/// </summary>
/// <param name="lock_str">锁定的资源名称</param>
/// <param name="timeout">超时次数(1秒1次)</param>
/// <returns></returns>
public static bool GetLock(string lock_str, long trycount)
{
if (trycount <= 0) trycount = 1;
var iDb = Common.GetIDb();
for (int i = 0; i < trycount; i++)
{
try
{
iDb.ExecuteSql("insert into db_lock(lock_str,lock_userid) values('" + lock_str + "','1')");
return true;
}
catch (Exception ex)
{
Thread.Sleep(1000);
}
}
return false;
}
/// <summary>
/// 释放分布式锁
/// </summary>
/// <param name="lock_str"></param>
public static void ReleaseLock(string lock_str)
{
try
{
var iDb = Common.GetIDb();
iDb.ExecuteSql("delete from db_lock where lock_str='" + lock_str + "' and lock_userid='1'");
}
catch { }
}
}
3、调用测试
class Program
{
static void Main(string[] args)
{
Console.WriteLine("开始获取锁[" + DateTime.Now.ToString("HH:mm:ss.fff") + "]...");
if (DBLock.GetLock("lock_demo", 3))
{
Console.WriteLine("获取到了锁[" + DateTime.Now.ToString("HH:mm:ss.fff") + "]...");
Thread.Sleep(10 * 1000);
DBLock.ReleaseLock("lock_demo");
Console.WriteLine("已释放锁[" + DateTime.Now.ToString("HH:mm:ss.fff") + "]!");
}
else
{
Console.WriteLine("获取锁失败[" + DateTime.Now.ToString("HH:mm:ss.fff") + "]!");
}
Console.WriteLine("ok");
Console.ReadLine();
}
}
测试效果如下:
另外,如果想提高锁的性能的话(减少不必要的锁竞争),记着使用双重判断:
if(true)
{
if (DBLock.GetLock("lock_demo", 3))
{
if(true)
{
//...
}
}
}
4、关于死锁以及处理方法
当程序获取锁后突然断电或意外情况导致没有主动释放锁,这种情况下是会发生死锁的,即使服务器重启也不行。为了解决这种问题,我们可以在程序中定时执行sql语句(每两分钟)或者是直接在数据库服务器上建立定时作业。
下面演示了在sqlsever2014上新建作业定时清除过期锁:
4.1 确保开启SQL Server代理
如果你的代理显示已禁用,可以尝试右键-》启动或启动SQL Server代理服务:
或者是参考:“代理 XP”服务器配置选项
4.2 新建作业
左侧选择“步骤”继续:
点击上面的“新建”:
上面的“步骤”设置完成后,开始设置“计划”,也就是执行的间隔,这里我们希望每3分钟运行一次:
新建完成后确定即可!
4.3 可以查看执行记录