起初做这个东西,是在一个内部组件中,用于在高并发的环境下得到一个较短的“相对”不重复标识字符串;(这里说的相对是指一定的数量下不重复)
灵感自然是来自于SqlServer的自增列和@@Identity变量
但是自从做完之后就有一个问题困扰这我,就是这个Current属性,这个属性的实用性其实非常的差
因为在高并发的环境中,使用Next()之后,即使立即使用Current属性得到的也是一个新的值,这点来说跟SqlServer的@@Identity是完全不同的
@@Identity的值无论并发多严重,你在同一个语句,或者说同一次会话中是不会改变的,除非该次会话新增了记录
(最近用了Oracle的序列对象,其中的sequence.CurrVal和@@Identity是一样的)
由于一直没有好的解决方案,而且没有这个功能不会影响这个类的使用,由于本身简单不需要维护,所以就渐渐遗忘了。
昨日无意间发现了StaticThreadAttribute这个特性,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 = _StaticId; 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属性的值,并不会受到其他线程的影响了
StaticThread特性允许我们将一个静态的变量的值在不同线程中是独立的
这个特性在某些情况下还是非常好用的
比如web应用中,每个请求都是一个独立的线程,如果我们希望将一个值作为静态字段全局使用,同时又不想影响其他用户,这时候一般我们是使用Session的
现在就可以有第二种选择了
Session可以将一个值长时间的保存,不于局限一次请求,但是Session需要应用System.Web.dll
StaticThread可以直接指定一个静态变量,但仅只能是当前请求,请求总段值就丢了
可以更新需要选择使用
最后,不知道StaticThread会不会带来额外的性能问题,希望知道的朋友可以告知
一 本系列随笔概览及产生的背景
近阶段接到一些b/s类型的软件项目,但是团队成员之前大部分没有这方面的开发经验,于是自己选择了一套目前网上比较容易上手的开发框架(FineUI),计划录制一套视频讲座,来讲解如何利用FineUI快速开发一个小型的b/s结构的管理系统,已达到帮助团队成员快速掌握b/s结构信息系统的开发方法。
源码位置:https://github.com/kamiba/FineUIDemo
源码下载办法:安装SVN客户端(本文最后提供下载地址),然后checkout以下的地址:https://github.com/kamiba/FineUIDemo.git
初学者应先下载源代码,然后对照视频,一步一步跟着做。
演示地址:http://42.121.4.78:8055/ 用户名和密码 均为admin
作者计划将本系列博文+视频做成一个Step by Step系列,请初级读者关注,高手绕道。目录拟定如下:
2.系统需求及数据库设计;
3.加工人员,产品,产品分类等基础信息管理(如何使用FineUI进行增删改查)
4.取货管理,送货管理,取送货统计,支付管理,支付统计(grid控件,分页,数据表查询)
5.将取送货管理系统整合进AppBox2.0(AppBox是使用FineUI开发的开源的通用权限管理框架,目前最新版本是3.0)。
二 为什么采用视频的方式讲解
缺点:
1.不利于SEO。
优点:
1.视频的方式相对写技术博文的效率更高(语音比文字在同样的时间内可以传达更多的信息);
2.讲述内容的性质决定的(这个系列的视频是从头开始讲解一个小型web系统是如何开发出来的,虽然没有什么高深的技术,但是涉及到很多开发中常用的技巧,比较适合初学者。采用视频的方式可以更加详细的把这些细节内容展现出来)。
三 不仅仅是FineUI
视频中,除了讲解FineUI的基本使用技巧之外,介绍了很多初级开发人员不是很理解但却十分重要的知识。比如数据库表的设计与查询方法,cookie的用法,表格的分页,程序集,添加引用,命名空间,postback表单回传的概念等等。
四 前2节视频内容简介
第一节 使用FineUI搭建系统基本框架
1.空web项目的创建;
2.登录对话框(登录成功后如何自动跳转到主界面,Cookie的运用,以使得服务器记录用户的认证状态);
3.主界面布局的讲解(Top Region,left Region,main Region,tree控件,TabStrip控件)。
4.模块配置文件(menu.xml)。
第二节 系统需求及数据库设计
1.系统需求分析
2.数据库表的创建(数据库的设计方法,如何保证数据的完整性和一致性,数据库设计中应该注意的问题)
3.动软代码生成器的使用(如何连接数据库,生成三层代码)
4.将动软代码生成器生成的代码合并到项目中
五 前两节视频下载地址及常用工具下载地址
1.FineUI+动软代码生成器开发一个b/s结构的取送货管理信息系统第一节
2.FineUI+动软代码生成器开发一个b/s结构的取送货管理信息系统第二节