zoukankan      html  css  js  c++  java
  • 线程安全问题

    问题:线程不安全,

    GetNextID中nextIds[BusinessIdKey]为空,没有这个键
    using Consul;
    using Newtonsoft.Json;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;
    using System.Xml.Serialization;
    using YesWay.Redis.Base;
    
    namespace YesWay.Redis
    {
        public class IdGenerator : RedisToolBase
        {
            //redis客户端对象
            private static readonly NedisClient client = new NedisClient(GetRedisConfig(redisConfigKey));
    
            //redis客户端对象配置存放在Consul服务端的key
            private static readonly string redisConfigKey = "redis/common/idgeneratorconfig";
            
            // 存放BusinessIdKey,MaxId
            private static readonly Dictionary<string, long> maxIds = new Dictionary<string, long>();
            
            // 存放BusinessIdKey,NextId
            private static readonly Dictionary<string, long> nextIds = new Dictionary<string, long>();
    
            private static readonly object objincrementsLock = new object();
            // 存放BusinessIdKey,id增量
            private static readonly Dictionary<string, long> increments = new Dictionary<string, long>();
    
            /// <summary>
            /// 计算主键时的增量
            /// </summary>
            private static readonly uint persistenceIncrement = 10;
    
            /// <summary>
            /// 业务IdKey
            /// </summary>
            private string busnessIdKey = string.Empty;
    
            /// <summary>
            /// 使用业务ID的key,ID增量初始化
            /// </summary>
            /// <param name="BusnessIdKey">业务IdKey</param>
            /// <param name="Increment">id增量,默认为1,不能大于10</param>
            public IdGenerator(string BusinessIdKey, uint Increment=1)
            {
                Init(BusinessIdKey, Increment);
            }
    
            /// <summary>
            /// 初始化increments,maxIds,nextIds字典
            /// </summary>
            /// <param name="BusinessIdKey"></param>
            /// <param name="Increment"></param>
            private void Init(string BusinessIdKey,long Increment)
            {
                if (!increments.ContainsKey(BusinessIdKey))
                {
                    lock (objincrementsLock)
                    {
                        if (!increments.ContainsKey(BusinessIdKey))
                        {
                            increments.Add(BusinessIdKey, Increment);
                            
                            nextIds.Add(BusinessIdKey, maxIds[BusinessIdKey] - persistenceIncrement);
                            maxIds.Add(BusinessIdKey, client.Increment(BusinessIdKey, persistenceIncrement));
    
                        }
                    }
                }
            }
    
            /// <summary>
            /// 重新设置MaxID
            /// </summary>
            /// <returns></returns>
            private static void ResetMaxID(string BusinessIdKey)
            {
                maxIds[BusinessIdKey] = client.Increment(BusinessIdKey, persistenceIncrement);
                nextIds[BusinessIdKey] = maxIds[BusinessIdKey] - persistenceIncrement;
            }
            
            // 获取下一个ID的锁
            private static readonly object nextIDLocker = new object();
    
            /// <summary>
            /// 根据业务Id键获取下一个主键ID
            /// </summary>
            /// <returns></returns>
            public Int64 GetNextID(string BusinessIdKey)
            {
                lock (nextIDLocker)
                {
                    nextIds[BusinessIdKey] = nextIds[BusinessIdKey] + 1;
    
                    // 如果自增后的行程ID大于已经持久的行程ID,则先持久行程ID,再返回
                    if (nextIds[BusinessIdKey] >= maxIds[BusinessIdKey])
                    {
                        ResetMaxID(BusinessIdKey);
                    }
    
                    return nextIds[BusinessIdKey];
                }
            }
        }
    }
    

      调用测试代码:

    //多线程测试
                for (int i = 0; i < 100; i++)
                {
                    ThreadStart num = new ThreadStart(GeneratorIDTest);
                    Thread numThread = new Thread(num);
                    numThread.Start();
    
                    ThreadStart num2 = new ThreadStart(GeneratorIDTest2);
                    Thread numThread2 = new Thread(num2);
                    numThread2.Start();
                }
                //Console.WriteLine("开始" + ids.Count() + "mainID:" + Thread.CurrentThread.ManagedThreadId.ToString());
                Thread.Sleep(10000);
                //Console.WriteLine("结束未去重:" + ids.Count() + "去重:" + ids.Distinct().Count());
                Console.WriteLine("safeIds结束未去重:" + safeIds.Count() + "去重:" + safeIds.Distinct().Count());
                Console.WriteLine("safeIds1结束未去重:" + safeIds1.Count() + "去重:" + safeIds1.Distinct().Count());
                Console.WriteLine("safeIds2结束未去重:" + safeIds2.Count() + "去重:" + safeIds2.Distinct().Count());
    

      mian.cs

    static ConcurrentQueue<long> safeIds = new ConcurrentQueue<long>();
            static ConcurrentQueue<long> safeIds1 = new ConcurrentQueue<long>();
            static ConcurrentQueue<long> safeIds2 = new ConcurrentQueue<long>();
            //static Queue<long> safeIds2 = new Queue<long>();
            private static void GeneratorIDTest()
            {
                var primaryKey = new IdGenerator("blog_id7", 1);
                for (int i = 0; i < 50; i++)
                {
                    var id = primaryKey.GetNextID("blog_id7");
                    //ids.Add(id);
                    safeIds.Enqueue(id);
                    safeIds1.Enqueue(id);
                    Console.WriteLine("线程ID"+Thread.CurrentThread.ManagedThreadId.ToString() +":"+id);
                }
            }
    
            private static void GeneratorIDTest2()
            {
                var primaryKey = new IdGenerator("blog_id8", 1);
                for (int i = 0; i < 50; i++)
                {
                    var id = primaryKey.GetNextID("blog_id8");
                    //ids.Add(id);
                    safeIds.Enqueue(id);
                    safeIds2.Enqueue(id);
                    Console.WriteLine("线程ID" + Thread.CurrentThread.ManagedThreadId.ToString() + ":" + id);
                }
            }
    

      错误原因:

    init方法中只判断了!increments.ContainsKey(BusinessIdKey)是否包含这个键,其它线程绕过,去执行getnext方法了

    解决办法:

    每个都需要判断,加锁,防止其它线程跳过init,去执行getnext方法

    /// <summary>
            /// 初始化increments,maxIds,nextIds字典
            /// </summary>
            /// <param name="BusinessIdKey"></param>
            /// <param name="Increment"></param>
            private void Init(string BusinessIdKey,long Increment)
            {
                if (!increments.ContainsKey(BusinessIdKey))
                {
                    lock (objIncrementsLock)
                    {
                        if (!increments.ContainsKey(BusinessIdKey))
                        {
                            increments.Add(BusinessIdKey, Increment);
                        }
                    }
                }
                if (!maxIds.ContainsKey(BusinessIdKey))
                {
                    lock (objMaxIdsLock)
                    {
                        if (!maxIds.ContainsKey(BusinessIdKey))
                        {
                            maxIds.Add(BusinessIdKey, client.Increment(BusinessIdKey, persistenceIncrement));
                        }
                    }
                }
                if (!nextIds.ContainsKey(BusinessIdKey))
                {
                    lock (objNextIdsLock)
                    {
                        if (!nextIds.ContainsKey(BusinessIdKey))
                        {
                            nextIds.Add(BusinessIdKey, maxIds[BusinessIdKey] - persistenceIncrement);
                        }
                    }
                }
            }
    

      

  • 相关阅读:
    CentOS虚拟机和物理机共享文件夹实现
    集训第六周 数学概念与方法 概率 数论 最大公约数 G题
    集训第六周 数学概念与方法 概率 F题
    集训第六周 E题
    集训第六周 古典概型 期望 D题 Discovering Gold 期望
    集训第六周 古典概型 期望 C题
    集训第六周 数学概念与方法 UVA 11181 条件概率
    集训第六周 数学概念与方法 UVA 11722 几何概型
    DAG模型(矩形嵌套)
    集训第五周 动态规划 K题 背包
  • 原文地址:https://www.cnblogs.com/liuqiyun/p/9494451.html
Copyright © 2011-2022 走看看