.net 同一时间戳下生成不重复的订单标识
为了避免在业务中出现订单号重复的情况,这里针对生产订单号的方法做了一定优化,基本可以避免同一时间戳下生成重复标识的问题。
思路: 假设订单编号由 平台标识(4位)+ 年份(4位) + 时间戳(10位) + 随机数(4位)组成的22位订单编号,由于前面8位固定的,
如果我们想要避免重复,就要避免 同一时间戳下生成重复的随机数。
代码如下
private static readonly object _lock = new object(); private static Dictionary<string, List<int>> _dic = new Dictionary<string, List<int>>(); private static long _num; /// <summary> /// 生成唯一平台订单号 /// </summary> /// <param name="ctype"></param> /// <returns></returns> public static string GeneratOrderNo(ContractType ctype) { lock (_lock) { TimeSpan timespan = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); string timeStamp = Convert.ToInt64(timespan.TotalSeconds).ToString(); //若时间戳 + 随机数重复, 重新生成随机数 string nonce = GenerateNonce(timeStamp); return ((int)ctype).ToString() + DateTime.Now.ToString("yyyy") + timeStamp + nonce; } } /// <summary> /// 生成不重复的四位随机数(同一时间戳下不允许重复) /// </summary> /// <param name="timeStamp">10位时间戳</param> /// <returns></returns> private static string GenerateNonce(string timeStamp) { Random random = new Random(RandomHelper.GetRandomSeed()); int nonce = random.Next(1000, 9999); if (_dic.TryGetValue(timeStamp, out List<int> value)) { //重新获取随机数 if (value.Contains(nonce)) { return GenerateNonce(timeStamp); } else { value.Add(nonce); _dic.Remove(timeStamp); _dic.Add(timeStamp, value); } } else { _dic.Clear(); _dic.Add(timeStamp, new List<int> { nonce }); } return nonce.ToString(); }
定义 lock 变量,可确保当一个线程位于代码的临界区时,另一个线程不会进入该临界区。
定义 _dic 变量,用键值对的方式存储时间戳 + 随机数(nonce),以时间戳 timeStamp 作 Key , 并保存同一时间戳 timeStamp 下的所有随机数集合,作为键值。
若为同一时间戳下生成的订单号,获取到已存的随机数集合进行匹配,如果重复则重新生成新的随机数 nonce,确保不会出现重复值。
static void Test() { string orderId = ""; //创建包含两个线程的数组 TaskFactory factory = new TaskFactory(); Task[] tasks = new Task[] { factory.StartNew(new Action(GenerateId)), factory.StartNew(()=> GenerateId()) }; Task.WaitAll(tasks); Console.WriteLine(orderId); } private static void GenerateId() { for (var i = 0; i < 100; i++) { orderId += GeneratOrderNo(ContractType.Tag) + System.Environment.NewLine; } orderId += "--------------------------------" + System.Environment.NewLine; }
另附上生成二十位随机数【10时间戳 + 10位递增值】的方法:
/// <summary> /// 根据时间戳生成20位编号 /// </summary> /// <returns></returns> public static string GenerateId() { lock (_lock)//lock 关键字可确保当一个线程位于代码的临界区时,另一个线程不会进入该临界区。 { if (_num == int.MaxValue) { _num = 0; } else { _num++; } TimeSpan timespan = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); string timeStamp = Convert.ToInt64(timespan.TotalSeconds).ToString(); Thread.Sleep(10); return timeStamp + _num.ToString().PadLeft(10, '0'); } }