首先我要说的是,MS asp.net在“处理页面时使用会话”核心中锁定了会话,这个锁定操作在asp.net 4版本的"webengine4.dll"动态链接库中。
从微软的角度来看,asp.net以正确的方式锁定了会话
,因为asp.net无法知道我们在会话中保存的信息类型,所以可以实现“最后修改者获胜”的正确性。
同样正确的是锁定会话的整个页面,因为这样可以给您的程序提供非常重要的同步机制。从现在的经验来看,我告诉您,大多数情况下都需要它。在我用我的会话替换了ms会话之后,在我所有的操作中都需要进行全局锁定,否则就会出现双重插入、双重操作等问题。
我给出一个例子来说明我在这里发现的问题。会话数据保存在SessionSateItemCollection中,这是一个键列表。当会话读取或写入此集合时,它们都是一起进行的。那么让我们看看这种情况。
我们在会话中有三个变量,“VAR1”,“VAR2”,“VAR3”
第一页。
getsession (实际上获取所有的数据并将它们放在列表中)
session[VAR1] = "data1";
savesession()
结果是VAR1=data1,VAR2=(var2的最后一个数据),VAR3=(var3的最后一个数据)
第二页。
getsession (实际上获取所有的数据并将它们放在列表中)
session[VAR2] = "data2";
savesession()
结果是VAR1=(var1的最后一个数据),VAR2=data2,VAR3=(var3的最后一个数据)
第三页。
getsession (实际上获取所有的数据并将它们放在列表中)
session[VAR3] = "data3";
savesession()
结果是 VAR1=(var1的最后一个数据), VAR2=(var2的最后一个数据) VAR3="data3"
请注意,每个页面都有数据的副本,因为它们从会话介质(例如,它们是从数据库中读取的)中读取了最近的数据。
如果我们让这3个页面在没有锁定的情况下运行,实际上我们并没有脏读,也不是“最后一次写入胜出”——我们所拥有的是“最后一次写入破坏其他写入”,因为如果你再想一想,当 VAR1 发生改变时,为什么 VAR2 和 VAR3 保持不变?如果你在其他地方更改了 VAR2,会怎样呢?
正因为这个原因,我们不能让这个系统没有锁定就运行。
现在假设有20个变量……完全混乱。
可能的解决方案
由于asp.net具有多池的多线程功能,保持跨池、跨计算机的相同数据的唯一方法是使用共同的数据库,或者像asp.net状态服务这样对所有进程都通用的程序。
我选择使用共同的数据库,因为这比创建一个专门用于此目的的程序更容易一些。
现在,如果我们将一个cookie与用户的会话数据连接起来,并使用完全自定义的数据库字段来控制数据,我们就可以知道要锁定什么、不锁定什么、如何保存或更改、如何比较和保持最后一次写入胜出的数据,从而使其正常工作,并完全禁用asp.net会话。尽管asp.net会话是用于所有需要的通用会话保留器,但不适用于像这种特殊情况。
asp.net是否可以在未来版本中使此工作?是的,通过增加一个称为“dirty”的额外字段,并在会话保存数据时使用“dirty”字段合并数据等方式,asp.net可以实现此目标。但是目前我所发现的情况是无法让它正常工作。
实际上,SessionStateItem在其属性上有一个脏标志,但未将其合并到保存数据的末尾。如果有人能为现有的ms asp.net会话状态保持器找到解决方案,那就太好了,我写下了我现在所发现的,但这并不意味着肯定没有其他方法 - 我只是没找到而已。
希望这一切都能有所帮助:)
自定义SessionStateModule
在这个答案中https://dev59.com/W3A65IYBdhLWcg3wyh57#3660837,James在编写自定义模块之后说:我仍然无法相信ASP.Net Session的自定义实现会锁定整个请求的会话。
SetAndReleaseItemExclusive
(保存值的方法)只有在应用了锁定时才会在ReleaseRequestState
事件期间被调用。如果有某种欺骗方式,我想从已经做过的人那里了解;我不可能是需要乐观并发会话的第一个人,我宁愿不要重复造轮子。 - Keith