zoukankan      html  css  js  c++  java
  • 对.Net状态保持机制和并发问题的思考

    最近在琢磨并发操作控制的问题,在此小小总结一下关于状态保持机制和并发解决方案,如有问题希望大家留言指正。

    并发问题分为几类:
    1.一个办理页面仅允许一个人进,第二个人再点就提示已经被别人访问,难点在于如何解决用户非法退出时放锁。
    2.一个人“签出”后另一个人无法办理。(这是情景1的简化版)
    3.多个人同时点“提交”按钮,但是系统应该只允许第一个人成功,后面的人都应该提交失败。
    4.一个人更新了表单的内容到数据库,另一个人w3wp进程的用户刷新后依旧读缓存而没读取数据库,缓存不同步。

    情景1和2已经在我另一篇帖子里解决了,这里主要讨论情景3和4的解决方案.

    对于这种提交操作,大体来讲可以把并发控制的执行流程分为“判锁-上锁-办事-解锁”四个阶段,“锁”是一个表,可以是哈希表,也可以是数据库表。对于提交/保存这样的情景,“办事”阶段仅仅是一个十毫秒级的简单操作,那么放在数据库里显然不妥,不仅严重增大了各方的压力,而且还存在延迟问题,不保证并发安全。

    接下来分析一下可能能够应用在并发控制当中的几种状态保持机制:
    1.Session是一种会话相关的状态保存方式,每当浏览器去访问w3wp进程请求页面时,都会在本地生成一个写有SessionId的Cookie,浏览器负责将SessionId加到Http请求报文当中,每新开一个浏览器进程SessionId都不同。在w3wp进程内(或是状态服务当中)每个SessionId对应一张KeyValue哈希表,也就是说一个浏览器进程是读不到另一个浏览器进程写的同名Session的,因为它和会话相关,所以显然不能用作并发控制。
    2.Application是一种“进程内”哈希表,问题就出在于它是和进程相关的,在多w3wp进程的情况下进程间不能共享Application,也难怪它起了这么个名字,于是它也不能拿来控制并发。
    3.Cache数据缓存,广泛应用在减少数据库访问压力上,但是目前没找到接口能把它写到进程外,它和Application一样同样存在多w3wp进程的问题,w3wp进程之间是不共享缓存的。

    如果不能用数据库,那么通过上面的分析,唯一要解决的问题就是这个可以跨w3wp进程的哈希表到底放在哪才能共用。我们先来解决多w3wp进程下情景4的问题。

    既然是多进程,显然每个进程都有一个id,于是我们可以在Application_Start的时候给每个进程创建一个名为进程id的文件夹,假设A和B同时通过两个进程查看一个表单数据,A提交了表单修改到数据库,数据库写入成功后,可以在B的文件夹下写一个名为Guid的文件,文件里写着表单的主键id,这是对提交过程的修改。对于读取过程,B在读取缓存之前,先去遍历自己的文件夹,如果发现有A写进去的文件,则直接清缓存读库,然后删了文件。

    上面的方案通过文件的方式实现了对数据修改的“通知”来使多进程间的缓存“伪同步”,但是硬盘IO毕竟有性能损耗,而且仔细分析一下,由于网络延迟,两个人在“判锁”的时候会同时成功进入后面的逻辑,有很严重的安全隐患。

    最后想到的办法就是用Memcached做分布式缓存了。

     
    分类: 解决方案

    总觉得教材上的东西要自己总结一遍看得才明白,用的时候copy也方便。这次总结协变和逆变的知识。

    首先是泛型接口的协变逆变。

    假设有如下两个类:

    复制代码
    public class Person
    {
    }
    
    public class Chinese : Person
    {
    }
    复制代码

    并且有自己写的所谓“集合类”:

    复制代码
    interface IMyList<T>
    {
    }
    
    class MyList<T> : IMyList<T>
    {
    }
    复制代码

    根据里氏转换原则,显然这句话是成立的:

    Person p = new Chinese();

    但这句话仅仅是套了个集合,怎么就报错了呢:

    IMyList<Person> personList = new MyList<Chinese>();

    盗用数学上的概念:Person和Chinese的继承关系是“Chinese->Person”,假设把“把一个类名用做IMyList的泛型占位符”这一“映射”记为F的话,F(Chinese)->F(Person)是不成立的,如果经过F这一“映射”想让新的关系继续成立,那么就需要把接口改为:

    interface IMyList<out T> { }

    这样一来就可以编译通过了,out是协变标记,那么IMyList这一映射就是协变的。

    同样,这句话也报错了:

    IMyList<Chinese> chineseList = new MyList<Person>();

    在这里,Person->Chinese这一继承关系压根就不存在,更别题这个F(Person)->F(Chinese)的关系了,如果想实现这一关系,就需要加上“逆变”记号:

    interface IMyList<in T> { }

    接下来是委托中的协变逆变。

    继续沿用Person和Chinese两个类,给出如下两个委托:

    public delegate void PersonDelegate(Person p);
    public delegate void ChineseDelegate(Chinese c);

    之后定义两个方法用于装进委托里(由于是控制台程序,所以是静态的):

    复制代码
    static void PersonFunc(Person p)
    {
    }
    
    static void ChineseFunc(Chinese c)
    {
    }
    复制代码

    然后在Main里给委托赋值:

    ChineseDelegate cDel = PersonFunc;
    PersonDelegate pDel = ChineseFunc;

    在第一行代码当中,接受Chinese类型参数委托cDel的里面可以放进一个接受Person类型参数的方法PersonFunc,这是“子类”接受了“父类”,也就是说委托是支持逆变的。

    但是第二行代码报错了,委托不支持协变,因为肯定不能从父类里“点”出一个子类的方法来执行!

    在.Net4.0当中微软为这两个泛型委托提供了协变和逆变功能,在.Net3.5当中是不支持协变和逆变的:

    public delegate void Action<in T> (T obj);
    
    public delegate TResult Func<in T, out TResult>(T arg);

    对于有泛型返回值的Func,返回值类型是协变的,这样就满足了“映射”后的继承关系保留,但是对于没有返回值的Action,不支持协变的原因同上。

     
    分类: C#语言
  • 相关阅读:
    HTML-标题
    HTML-属性
    HTML-元素
    前端 vue Request Payload VS Form Data
    JWT,Session、Cookie(httpOnly,secure) 登录认证
    js 定位当前城市(ip,省份,城市,邮编)接口定位(搜狐、新浪、百度、腾讯API)
    sqlsugar 使用汇总
    asp.net 跨域
    asp.net core程序发布(Webapi,web网站)
    .net 多线程发展1.0--4.0
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2610829.html
Copyright © 2011-2022 走看看