• 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

  • 相关阅读:
    LintCode Python 简单级题目 488.快乐数
    LintCode Python 简单级题目 100.删除排序数组中的重复数字 101.删除排序数组中的重复数字II
    LintCode Python 简单级题目 373.奇偶分割数组
    LintCode Python 简单级题目 39.恢复旋转排序数组
    LintCode Python 简单级题目 35.翻转链表
    LintCode Python 简单级题目 451.两两交换链表中的节点
    LintCode Python 简单级题目 174.删除链表中倒数第n个节点
    aws查看官方centos镜像imageid
    linux shell脚本查找重复行/查找非重复行/去除重复行/重复行统计
    php配置优化-生产环境应用版
  • 原文地址:https://www.cnblogs.com/blqw/p/Identity.html
Copyright © 2020-2023  润新知