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);
                        }
                    }
                }
            }
    

      

  • 相关阅读:
    php数组之选择排序算法参考
    php数组之插入排序算法参考
    php数组之冒泡排序算法参考
    MySQL查询中的条件控制(where,group by,having,order by,limit,from,exists)
    第四次作业
    第三次作业
    第二次作业
    作业
    DevExpress TreeList使用心得
    项目开发总结:解决多线程窗体与主风格不一致问题
  • 原文地址:https://www.cnblogs.com/liuqiyun/p/9494451.html
Copyright © 2011-2022 走看看