線程安全問題

問題:線程不安全,redis

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

  錯誤緣由:ide

init方法中只判斷了!increments.ContainsKey(BusinessIdKey)是否包含這個鍵,其它線程繞過,去執行getnext方法了測試

解決辦法:ui

每一個都須要判斷,加鎖,防止其它線程跳過init,去執行getnext方法spa

 

/// <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);
                    }
                }
            }
        }
相關文章
相關標籤/搜索