• C# 订单流水号生成


    例如流水号格式如下:XX201604120001,2位前缀加8位日期加4位流水号

    首先各种搜索出现如下解决方案

        public class SerialNoHelper
        {
            /// <summary>
            /// 生成流水号
            /// </summary>
            /// <param name="serialno">从数据库读取最大的流水号</param>
            /// <returns></returns>
            public String Generate(String serialno)
            {
                var today = DateTime.Today.ToString("yyyyMMdd");
    
                if (String.IsNullOrEmpty(serialno))
                    return $"XX{today}0001";
    
                var date = serialno.Substring(2, 8);
                if (date == today)
                {
                    var no = Convert.ToInt32(serialno.Substring(10));
                    return $"XX{today}{++no:0000}";
                }
    
                return $"XX{today}0001";
            }
        }
    

    然后测试

      class Program
        {
            static void Main(string[] args)
            {
                //模拟数据库
                var array = new List<String>();
    
                //模拟订单号生成
                var tasks = new Task[1000];
                for (var i = 0; i < tasks.Length; i++)
                {
                    tasks[i] = Task.Run(() =>
                    {
                        var helper = new SerialNoHelper();
    
                        var sno = array.LastOrDefault();//模拟从数据库读取最大的流水号
    
                        var serialno = helper.Generate(sno);
    
                //各种逻辑操作
    array.Add(serialno);//模拟保存到数据库 Console.WriteLine(serialno); }); } //等待执行完成 Task.WaitAll(tasks); Console.WriteLine("-----------------------------------分割线-----------------------------------"); //测试是否重复 var repeat = array.GroupBy(m => m).Where(m => m.Count() > 1).Select(m => m.Key).ToList(); foreach (var item in repeat) Console.WriteLine(item); Console.ReadLine(); } }

    测试后不难发现,在高并发下很容易就出现重复。

    好像哪里不对啊,修改SerialNoHelper类实现单例,然后给Generate方法加锁。这下应该可以了吧。

        public sealed class SerialNoHelper
        {
            private static volatile SerialNoHelper helper;
            private static readonly Object syncRoot = new Object();
    
            private SerialNoHelper()
            {
            }
    
            public static SerialNoHelper Helper
            {
                get
                {
                    if (helper == null)
                    {
                        lock (syncRoot)
                        {
                            if (helper == null)
                                helper = new SerialNoHelper();
                        }
                    }
                    return helper;
                }
            }
    
            /// <summary>
            /// 生成流水号
            /// </summary>
            /// <param name="serialno">从数据库读取最大的流水号</param>
            /// <returns></returns>
            public String Generate(String serialno)
            {
                lock (syncRoot)
                {
                    var today = DateTime.Today.ToString("yyyyMMdd");
    
                    if (String.IsNullOrEmpty(serialno))
                        return $"XX{today}0001";
    
                    var date = serialno.Substring(2, 8);
                    if (date == today)
                    {
                        var no = Convert.ToInt32(serialno.Substring(10));
                        return $"XX{today}{++no:0000}";
                    }
    
                    return $"XX{today}0001";
                }
            }
        }
    

    心情忐忑的按下F5,WTF,居然还是有重复。

    不慌,走到窗口猛吸两口雾霾压压惊。接下来分析一下为什么还是会出现重复呢?

    生成序列号的时候依赖的是从数据库获取最大的流水号,但是在生成序列号之后,到保存序列号到数据库这之间一般会有一些逻辑操作。

    这就导致在高并发的时候,前一个流水号还没有保存到数据库,那就有可能从数据库获取到的流水号是相同的,那么生成的流水号自然就会出现重复。

    怎么解决这个问题呢?在Generate方法内就把生成的流水号保存到数据库?这显然不太合适,上面提到保存流水号到数据库一般会有一些逻辑操作。

    最终版本

    public sealed class SerialNoHelper
        {
            private static volatile SerialNoHelper helper;
            private static readonly Object syncRoot = new Object();
    
            private static String lastdate;
            private static Int32 lastno;
    
            private SerialNoHelper()
            {
            }
    
            public static SerialNoHelper Helper
            {
                get
                {
                    if (helper == null)
                    {
                        lock (syncRoot)
                        {
                            if (helper == null)
                                helper = new SerialNoHelper();
                        }
                    }
                    return helper;
                }
            }
    
            /// <summary>
            /// 生成流水号
            /// </summary>
            /// <param name="serialno">从数据库读取最大的流水号</param>
            /// <returns></returns>
            public String Generate(String serialno)
            {
                lock (syncRoot)
                {
                    var today = DateTime.Today.ToString("yyyyMMdd");
    
                    if (today == lastdate)
                        return $"XX{today}{++lastno:0000}";
    
                    lastdate = today;
                    lastno = 0;
    if (!String.IsNullOrEmpty(serialno) && serialno.Substring(2, 8) == today) lastno = Convert.ToInt32(serialno.Substring(10)); return $"XX{today}{++lastno:0000}"; } } }

    终于成功了。

    当然这种处理方式也有不好的地方。

    比如当生成流水号最终没有使用,会造成浪费。

    最后

    感谢阅读,希望可以帮到你。也欢迎留言指正文中的错误与不足,大家共同进步。

  • 相关阅读:
    PAT-乙级-1011. A+B和C (15)
    PAT-乙级-1010. 一元多项式求导 (25)
    PAT-乙级-1009. *说反话 (20)
    PAT-乙级-1008. 数组元素循环右移问题 (20)
    PAT-乙级-1007. 素数对猜想 (20)
    PAT-乙级-1006. 换个格式输出整数 (15)
    PAT-乙级-1005. 继续(3n+1)猜想 (25)
    PAT-乙级-1004. 成绩排名 (20)
    BZOJ 1030: [JSOI2007]文本生成器
    BZOJ 2938: [Poi2000]病毒
  • 原文地址:https://www.cnblogs.com/odduncle/p/5382703.html
Copyright © 2020-2023  润新知