博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Asp.Net在多线程环境下的状态存储问题
阅读量:5152 次
发布时间:2019-06-13

本文共 3838 字,大约阅读时间需要 12 分钟。

      在应用开发中,我们经常需要设置一些上下文(Context)信息,这些上下文信息一般基于当前的会话(Session),比如当前登录用户的个人信息;或者基于当前方法调用栈,比如在同一个调用中涉及的多个层次之间数据。

      在.Net中,常用的有以下三种方法来实现这个特性.

      HttpContext.Current.Session或HttpContext.Currnet.Items是大家使用的最多的方式.

      [ThreadStatic]方式可以存储单个线程的共享状态.

      System.Runtime.Remoting.Messaging.CallContext类则可以存储一个逻辑线程的共享状态,即主线程和其所有子线程都共享这段内存.

      在Asp.Net中通常使用第一种方式.但是鱼李,指出HttpContext.Current并非无处不在,只有是由请求发起的线程,HttpContext.Current才不为空.换句话说,在多线程环境下, 比如是由定时器发起的线程,Currnet属性就为空,这时依赖于它的相关功能便无法完成.比如使用它获取文件路径等.

      鱼李给出了两种解决方法,将HttpContext.Current保存在外部变量中,或者通过函数参数传递.

      我个人认为这只是折中的解决办法,它没有在解决问题的同时保待简单性,即程序员不需关心HttpContext.Current的保存位置与传递方式,而直接便可使用.

      后台又查到了A大的.给出了保存下文(Context)信息的通用解决方案,简单来说,在桌面环境使用System.Runtime.Remoting.Messaging.CallContext类,在Web环境下使用常规的HttpContext.Current,但是同时在System.Runtime.Remoting.Messaging.CallContext类中也保存了一个对它的引用.在线程切换时,依照.Net的设计,System.Runtime.Remoting.Messaging.CallContext类中保存的实现了ILogicalThreadAffinative接口的数据都会自动被复制到新的线程中,即完成了上下文传递.

      A大给出了一个精妙的解决方案,但却没有解决我所有的疑问,比如48L的园友就提出了"请教一下,callcontxt无论是那种应用都可以使用,为什么还要使用HttpSessionState?".于是我继续探究.终于在一篇老外的中找到了答案.

      在那篇文章里,老外做了一个试验.有两个页面,均在其构造函数与Page_Load中使用上面三种方式记录当前线程Id,但是在名为slow的页面中人为让处理线程睡一下来模拟耗时操作.首先访问slow页面,在其返回前快速多次刷新fast页面.最终的打印结果让作者surprise了一下.对于slow页面,执行构造函数的线程与Page_Load的线程保持一致,三种方式的记录结果也没有丢失,但是在fast页面中,有可能出现执行构造函数的线程与Page_Load的线程不一致,三种方式的记录结果也丢失了两种:仅剩下HttpContext了.

      我的重现代码如下,增加了LogicalSetData方式,后文再表:

public partial class Fast : System.Web.UI.Page{    [ThreadStatic]    private static string info = string.Empty;    public Fast()    {        info = "fast ctor:" + Thread.CurrentThread.ManagedThreadId;        CallContext.SetData("id", Thread.CurrentThread.ManagedThreadId);        CallContext.LogicalSetData("id1", Thread.CurrentThread.ManagedThreadId);        Items["id2"] = Thread.CurrentThread.ManagedThreadId;    }    protected void Page_Load(object sender, EventArgs e)    {        Response.Write("ThreadStatic:" + info);        info = string.Empty;        Response.Write("
"); Response.Write("CallContext.SetData:" + CallContext.GetData("id")); Response.Write("
"); Response.Write("CallContext.LogicalSetData:" + CallContext.LogicalGetData("id1")); Response.Write("
"); Response.Write("Items:" + Items["id2"]); Response.Write("
"); Response.Write("
fast page_load:" + Thread.CurrentThread.ManagedThreadId); }}public partial class Slow : System.Web.UI.Page{ [ThreadStatic] private static string info = string.Empty; public Slow() { Thread.Sleep(1000); info = "slow ctor:" + Thread.CurrentThread.ManagedThreadId; CallContext.SetData("id", Thread.CurrentThread.ManagedThreadId); CallContext.LogicalSetData("id1", Thread.CurrentThread.ManagedThreadId); Items["id2"] = Thread.CurrentThread.ManagedThreadId; } protected void Page_Load(object sender, EventArgs e) { Thread.Sleep(1000); Response.Write("ThreadStatic:" + info); info = string.Empty; Response.Write("
"); Response.Write("CallContext.SetData:" + CallContext.GetData("id")); Response.Write("
"); Response.Write("CallContext.LogicalSetData:" + CallContext.LogicalGetData("id1")); Response.Write("
"); Response.Write("Items:" + Items["id2"]); Response.Write("
"); Response.Write("
slow page_load:" + Thread.CurrentThread.ManagedThreadId); }}

 

      执行结果如下:

      从博文中获知,这是完全正常的执行结果, Asp.Net开发团队还为其起了个好听的名字:Thread-Agile.这个特性表明,即使你使用常规的Asp.Net开发方式,也不能保证所有的代码一定会在同一线程中执行.从其它的文章中获知,这是与负载有关的.负载越大,越有可能产生多线程.每当程序进入一个新的线程中执行时,Asp.Net会手动(是使用额外代码实现的,不是.Net自带的机制)将HttpContext对象复制到新线程中.一方面这能将多线程完全透明,让程序员使用单线程的编程方式编写多线程程序;另一方面CallContext.SetData与[ThreadStatic]就丢失了.但由于使用LogicalSetData方法存储的数据其内部都会自动封装成实现了ILogicalThreadAffinative接口的对象,所以在线程切换时能正常流转.

      所以,在Asp.Net环境下,除非自己建立一套上下文环境解决方案,否则在该用的情况下还是老老实实使用HttpContext吧.

      欢迎各路朋友指正.

 

      参考

     

     

     

     

     

     

     

     

     

     

     

转载于:https://www.cnblogs.com/ljzforever/p/3142782.html

你可能感兴趣的文章
001.RAID简介
查看>>
投标项目的脚本练习2
查看>>
第五次实验
查看>>
201521123107 《Java程序设计》第9周学习总结
查看>>
runtime的基本应用
查看>>
关于scrollTop的那些事
查看>>
Caroline--chochukmo
查看>>
算法导论笔记 第8章 线性时间排序
查看>>
利用jquery的contains实现搜索功能
查看>>
Xcode 6.2 beta 3 太难下载!下载了,不敢独享
查看>>
并发编程
查看>>
Bootstrap
查看>>
C语言错误: HEAP CORRUPTION DETECTED
查看>>
【Java基础】Java类的加载和对象创建流程的详细分析
查看>>
2018-2019-1 20165231《信息安全系统设计基础》第二周学习总结
查看>>
iOS之文本属性Attributes的使用
查看>>
从.Net版本演变看String和StringBuilder性能之争
查看>>
Excel操作 Microsoft.Office.Interop.Excel.dll的使用
查看>>
XlFileFormat
查看>>
Windows消息机制(转)1
查看>>