zoukankan      html  css  js  c++  java
  • ThreadStatic应用(Identity补完)

    关于Identity

    Identity自增序列/唯一断标识

    起初做这个东西,是在一个内部组件中,用于在高并发的环境下得到一个较短的“相对”不重复标识字符串;(这里说的相对是指一定的数量下不重复)

    灵感自然是来自于SqlServer的自增列和@@Identity变量

    困扰

    但是自从做完之后就有一个问题困扰这我,就是这个Current属性,这个属性的实用性其实非常的差

    因为在高并发的环境中,使用Next()之后,即使立即使用Current属性得到的也是一个新的值,这点来说跟SqlServer的@@Identity是完全不同的

    @@Identity的值无论并发多严重,你在同一个语句,或者说同一次会话中是不会改变的,除非该次会话新增了记录

    (最近用了Oracle的序列对象,其中的sequence.CurrVal和@@Identity是一样的)

    由于一直没有好的解决方案,而且没有这个功能不会影响这个类的使用,由于本身简单不需要维护,所以就渐渐遗忘了。

    发现

    昨日无意间发现了ThreadStaticAttribute这个特性,MSDN官方文档对其的描述是:

    用 ThreadStaticAttribute 标记的 static 字段不在线程之间共享。 每个执行线程都有单独的字段实例,并且独立地设置及获取该字段的值。 如果在不同的线程中访问该字段,则该字段将包含不同的值。

    看了说明之后,我立即想起了这个被遗忘在角落的对象。逐动手改造

    修改代码

    using System;
    
    namespace blqw
    {
        /// <summary> 自增序列,最大0xFFFFFFF,超过0xFFFFFFF回归1
        /// </summary>
        public static class Identity
        {
            [ThreadStatic]
            static int? _ThreadCurrentId;
    
            static int _StaticId = 0;
            /// <summary> 当前值
            /// </summary>
            public static int Current
            {
                get { return _ThreadCurrentId.GetValueOrDefault(-1); }
            }
            /// <summary> 当前值的String形式
            /// </summary>
            public static string CurrentString
            {
                get
                {
                    if (_ThreadCurrentId.HasValue)
                    {
                        return GetString(_ThreadCurrentId.Value);
                    }
                    else
                    {
                        return null;
                    }
                }
            }
            /// <summary> 获取一个id,每获取一次就会自增1,该方法在所有线程都是安全的
            /// </summary>
            public static int Next()
            {
                int i = System.Threading.Interlocked.Increment(ref _StaticId);
                _ThreadCurrentId = i;
                if (i > (0xFFFFFFF))
                {
                    i = i - 0xFFFFFFF;
                    System.Threading.Interlocked.Exchange(ref _StaticId, i);
                }
                return i;
            }
    
            //字符字典
            static char[] _CharMap = new[]
            {
                'a','b','c','d','e','f','g','h','i','j','k','l','m',
                'n','o','p','q','r','s','t','u','v','w','x','y','z',
                'A','B','C','D','E','F','G','H','I','J','K','L','M',
                'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
            };
    
    
            /// <summary> 获取一个id的String表示形式,每获取一次就会自增1,该方法在所有线程都是安全的
            /// </summary>
            public static string NextString()
            {
                int number = Next();
                return GetString(number);
            }
    
            public static string GetString(int number)
            {
                if (number < 0)
                {
                    throw new ArgumentOutOfRangeException("number", "number不能小于0");
                }
                int length = (int)Math.Log(number, 52) + 1;//52比较合理,不要改了
                char[] c = new char[length];
                for (int i = length - 1; i > 0; i--)
                {
                    c[i] = _CharMap[number % 52];
                    number = number / 52;
                }
                c[0] = _CharMap[number];
                return new string(c);
            }
        }
    }

    测试

    for (int i = 0; i < 10; i++)
    {
        new Thread(o =>
        {
            Console.WriteLine(o + " > Init: " + Identity.Current);
            Thread.Sleep(1100);
            Console.WriteLine(o + " > Next: " + Identity.Next());
            Thread.Sleep(100);
            Console.WriteLine(o + " > Curr: " + Identity.Current);
            Thread.Sleep(100);
            Console.WriteLine(o + " > NStr: " + Identity.NextString());
            Thread.Sleep(100);
            Console.WriteLine(o + " > Curr: " + Identity.Current);
        }).Start(i);
        Thread.Sleep(100);
    }

    测试代码比较简单,开10个线程,让他们交错运行就行了

    先上一个没有标记StaticThread的结果

    把StaticThread注释掉运行,可以看到结果 在0号线程中使用Next得到值1 然后使用Current却得到2 ,因为在1号线程中调用Next影响了Current的值

    这就是原来的效果,所以之前使用的时候都是 int i = Identity.Next() ;然后就拿 i 使用了

    现在加在StaticThread

    可以看到,现在线程0中的Current属性的值,并不会受到其他线程的影响了

    总结

    ThreadStatic特性允许我们将一个静态的变量的值在不同线程中是独立的

    这个特性在某些情况下还是非常好用的

    比如web应用中,每个请求都是一个独立的线程,如果我们希望将一个值作为静态字段全局使用,同时又不想影响其他用户,这时候一般我们是使用Session的

    现在就可以有第二种选择了

    Session可以将一个值长时间的保存,不于局限一次请求,但是Session需要应用System.Web.dll

    ThreadStatic可以直接指定一个静态变量,但仅只能是当前请求,请求总段值就丢了

    可以更新需要选择使用

    最后,不知道ThreadStatic会不会带来额外的性能问题,希望知道的朋友可以告知

    code

    https://code.csdn.net/snippets/104950

  • 相关阅读:
    HBase导入数据同时与Phoenix实现同步映射
    Hive导入数据到HBase,再与Phoenix映射同步
    CDH5.16.1离线集成Phoenix
    设计原则学习笔记
    Maven安装配置
    SpringBoot之Mybatis操作中使用Redis做缓存
    Linux服务器防火墙白名单设置
    Linux查看端口占用情况,并强制释放占用的端口
    shell脚本切割tomcat日志文件
    mysql读写分离
  • 原文地址:https://www.cnblogs.com/blqw/p/Identity.html
Copyright © 2011-2022 走看看