DataLock | 数据锁、单据号码锁、并发锁(C#源码)|C/S开发框架
作者:csframework|C/S框架网  发布日期:2022/02/11 13:25:39

DataLock | 数据锁、单据号码锁、并发锁(C#源码)|C/S开发框架

DataLock - 数据锁、单据号码锁、并发锁

一、DataLock说明

数据机制用于管理对共享资源的并发访问,确保数据的完整性和一致性。DataLock数据锁主要用于在多用户操作环境下,避免多用户并发操作,导致脏写、脏读以及脏数据形成。

二、应用场景

  • 数据锁,用于处理特定的数据,比如用户A必须独占处理某种数据,执行数据锁后,用户B必须等待用户A处理完毕。
  • 单据号码锁,用于生成唯一的单据号码,不会并发操作!
  • 并发锁,处理有关并发方面的需求。

三、使用方法

3.1 数据锁标准使用方法

C# 全选
  public string DoSomething()
        {
            //实例化锁
            var dataLock = new DataLock(db_System, "LockName", "测试业务锁");

            try
            {
                dataLock.SetLock(); //锁定

                //
                //在这里编写处理逻辑
                //

                dataLock.Release();//处理完成:释放锁
                return "测试成功!";
            }
            catch (Exception ex)
            {
                dataLock.Release();
                throw ex;
            }
        }

3.2 生成唯一的单据号码、流水号码

C# 全选
  //获取最新的GroupNo,物流编号
        public long GetNewGroupNo()
        {
            //单据号码锁
            var dataLock = new DataLock(db_System, "LogisticGroup", "物流编号-单据号码锁");

            try
            {
                dataLock.SetLock(); //锁定

                long lastNo = GetLastGroupNo();//获取业务单据,最大的单据号码, MAX(NO)
                long groupNo;

                //第一次初始化锁
                if (String.IsNullOrEmpty(dataLock.LockData.Data))
                    dataLock.LockData.Data = lastNo.ToString();

                //获取数据锁最新的号码
                groupNo = ConvertEx.ToLong(dataLock.LockData.Data);//字符串转long
                if (groupNo == 0) groupNo = lastNo;

                //若数据锁中的号码小于单据最大的号码
                if (groupNo < lastNo) groupNo = lastNo;

                groupNo++;//号码加1

                dataLock.SetData(groupNo.ToString());//设置数据
                dataLock.Release();//释放锁

                return groupNo;
            }
            catch (Exception ex)
            {
                dataLock.Release();
                throw ex;
            }
        }

四、DataLock 源码

C# 全选
    /// <summary>
    /// DataLock - 数据锁、单据号码锁、并发锁(C#源码)
    /// </summary>
    public class DataLock
    {
        private DbFramework _EF;
        private string _lockName;
        private string _note;
        private string _token;
        private int _MAX_TRY_COUNT = 10;

        private sys_DataLock _Data = null;
        public sys_DataLock LockData { get { return _Data; } }

        /// <summary>
        /// 构造器
        /// </summary>
        /// <param name="db">CSFramework.DB数据库组件</param>
        /// <param name="lockName">数据锁名称</param>
        /// <param name="note">备注</param>
        /// <param name="maxTryCount">锁占用状态下, 尝试重新锁定的次数</param>
        public DataLock(IDatabase db, string lockName, string note = null, int maxTryCount = 10)
        {
            _lockName = lockName;
            _note = note;
            _MAX_TRY_COUNT = maxTryCount;
            _token = GetToken();//当前实例的token,重要!!!
            _EF = new DbFramework(db);
        }

        /// <summary>
        /// 是否锁定状态
        /// </summary>
        /// <returns></returns>
        public bool IsLock()
        {
            if (_Data == null)
            {
                _Data = GetLock();
                if (_Data == null) _Data = AddLock();//添加锁
            }

            return _Data.IsLock;
        }

        /// <summary>
        /// 锁定
        /// </summary>
        public void SetLock()
        {

            //尝试锁定10次
            for (int i = 1; i <= _MAX_TRY_COUNT; i++)
            {
                _Data = GetLock();//获取锁数据

                if (_Data == null) _Data = AddLock();//添加锁

                if (_Data.IsLock == false)
                {
                    _Data.IsLock = true;
                    _Data.AccessTime = DateTime.Now;
                    _Data.AccessToken = _token; //当前token
                    _EF.UpdateObject<sys_DataLock>(_Data);
                    return; //设置锁成功
                }
                else
                {
                    //已经是锁定状态,等待其他用户释放锁
                    Thread.Sleep(1000);
                }
            }

            throw new Exception($"设置数据锁<{_lockName}>失败");
        }

        /// <summary>
        /// 设置锁的关联数据
        /// </summary>
        /// <param name="data">关联数据</param>
        /// <param name="note">备注</param>        
        public void SetData(string data, string note = null)
        {
            _Data.Data = data;
            _Data.Note = String.IsNullOrEmpty(note) ? _Data.Note : note;
            _Data.AccessTime = DateTime.Now;

            //提交数据
            //_EF.UpdateObject<sys_DataLock>(_Data);
            _EF.UpdateObject<sys_DataLock>(_Data, null, null, new string[] { "Data", "Note", "AccessTime" });
        }

        /// <summary>
        /// 释放锁, 只可释放自己的锁!!!根据token判断
        /// </summary>
        public void Release()
        {
            _Data.IsLock = false;
            _Data.AccessTime = DateTime.Now;
            _EF.UpdateObject<sys_DataLock>(_Data, new string[] { "AccessToken" });
        }

        private sys_DataLock AddLock()
        {
            sys_DataLock o = new sys_DataLock
            {
                LockName = this._lockName,
                Note = this._note,
                IsLock = false,//默认不锁定
                AccessTime = DateTime.Now,
                AccessToken = _token,//当前token
            };

            bool ok = _EF.AddObject(o);
            if (ok)
                return o;
            else
                throw new Exception("添加锁失败!");
        }

        private sys_DataLock GetLock()
        {
            return _EF.Select<sys_DataLock>("LockName", _lockName);//根据key获取锁
        }

        private string GetToken()
        {
            long result = 1;
            int index = 1;
            byte[] bs = Guid.NewGuid().ToByteArray();

            foreach (byte b in bs)
            {
                result *= ((int)b + index * 2);
                index++;
            }

            var hex = string.Format("{0:x}", result);
            return hex;
        }

    }

五、sys_DataLock 表结构

C# 全选
    /// <summary>
    /// 数据锁的模型
    /// </summary>
    public class sys_DataLock
    {
        [IgnoreField]
        public int isid { get; set; }

        /// <summary>
        /// 锁名称
        /// </summary>
        [KeyField]
        public string LockName { get; set; }

        /// <summary>
        /// 是否锁定状态
        /// </summary>
        public bool IsLock { get; set; }

        /// <summary>
        /// 关联数据,自定义数据
        /// </summary>
        public string Data { get; set; }

        /// <summary>
        /// 备注信息
        /// </summary>
        public string Note { get; set; }

        /// <summary>
        /// 最新操作锁的时间
        /// </summary>
        public DateTime AccessTime { get; set; }

        /// <summary>
        /// 当前访问锁的用户标识(token)
        /// </summary>
        public string AccessToken { get; set; }

    }

六、依赖项目

CSFramework.DB

https://www.cscode.net/archive/csframework.db/1630589248.html

DataLock | 数据锁、单据号码锁、并发锁(C#源码)|C/S开发框架

C/S框架网|原创精神.创造价值.打造精品


扫一扫加作者微信
C/S框架网作者微信 C/S框架网|原创作品.质量保障.竭诚为您服务
上一篇 下一篇