文档章节

.net开发笔记(十六) 对前部分文章的一些补充和总结

IT周见智
 IT周见智
发布于 2015/06/05 17:17
字数 2795
阅读 4
收藏 0
点赞 0
评论 0

补充有两个:

  1. 一个是系列(五)中讲到的事件编程(网址链接),该文提及到了事件编程的几种方式以及容易引起的一些异常,本文补充“多线程事件编程”这一块。
  2. 第二个是前三篇博客中提及到的“泵”结构在编程中的应用,我稍微做一点补充。

总结有一个:

  1. 如果您善于总结和类比,您会发现世界好多东西其实都是一样的。这部分主要理清楚框架时代中的框架和我们coder所写代码之间的关联。

下面是正文:

多线程事件编程

    系列(五)中提及到了事件在注册和注销时,系统已经做了多线程处理,只是不太完美(以this为锁对象,this是public的,锁对象是不能对外公开的),后来通过自己定义锁对象加锁来实现的。可是该篇文章并没有提到在类内部激发事件时可能引发的异常:

 1 class Subject
 2 {
 3      XXEventHandler _xx;
 4      object _xxSync = new Object();
 5      public event XXEventHandler XX
 6      {
 7          add
 8          {
 9               lock(_xxSync)
10               {
11                    _xx = (XXEventHandler)Delegate.Combine(_xx,value);
12               }
13          }
14          remove
15          {
16               lock(_xxSync)
17               {
18                   _xx = (XXEventHandler)Delegate.Remove(_xx,value);
19               }
20          }
21      }
22     protected virtual void OnXX(XXEventArgs e)
23     {
24          if(_xx != null)
25          {
26               _xx(this,e);
27          }
28     }
29    public void DoSomething()
30    {
31          //
32          OnXX(new XXEventArgs(…));
33    }
34 }
View Code

如上代码所述,在多线程情况下,if(_xx != null)这行代码执行为true后,在执行下一行_xx(this,e);之前,_xx可能已经为null,引发异常理所当然。解决方法很简单,照葫芦画瓢,在OnXX中加锁,源代码变为:

 1     protected virtual void OnXX(XXEventArgs e)
 2     {
 3          lock(_xxSync)
 4          {
 5             if(_xx != null)
 6             {
 7               _xx(this,e);
 8             }
 9          }
10     }
View Code

没错,这样确实能解决激发事件时有可能引发的异常,但如果仅仅是为了说明该方法可以解决问题的话,我是不会特大篇幅来说明它的。我们来看另外一种巧妙解决方法:

1     protected virtual void OnXX(XXEventArgs e)
2     {
3          XXEventHandler xx = _xx;
4          if(xx != null)
5          {
6             xx(this,e);
7          }
8     }
View Code

如上代码所述,在判断_xx是否为null之前,我们先用一个临时变量代替它,之后将使用_xx的地方全部替换为xx。这样,就不用担心xx会由其它线程改变为null了,因为xx对其他线程不可见。这个原理很简单,委托链是不可改变的(Delegates is immutable),也就是说,我们注册或者注销事件时,并不是在原来的委托链表基础上进行增加或者删除节点,而是每次都是重新生成了一个全新链表再赋给委托变量。其实这个诸位可以找到规律,我们在注册注销事件时,一般obj.Event+=…或者Event = Delegate.Combine(…) Event = Delegate.Remove(),可以看出,每次都是将一个全新的值赋给原来委托变量,并没有在原来链表基础上进行操作,因此,_xx和xx虽然同是指向同一链表,但是我们注销注册事件时,只是让_xx指向另外一个链表而已,原链表(xx)并没有变。

    这个其实就是我们刚学习编程的时候,使用值传递调用方法时,实参将值传递给了形参,形参如果改变了(被重新赋值),实参的值是不会变的。指针(引用)也一样,形参指向了另外一个对象,实参还是指向原来的对象。

“泵”结构的另外一种方式

    系列(十三)中(网址链接)讲到,在泵结构中,如果在获取数据环节直接处理数据容易降低获取数据的效率,也就是说,最好不要一获取到数据就处理它,因为处理数据大多数情况下是一个耗时过程,数据处理结束前,下一次“数据获取”不能开始,影响获取数据的效率。如下图:

图1

如图所示,处理数据在泵循环体内,数据处理结束之前,缓冲区中的数据就会大量积累。我们当时的做法是,获取数据后不马上进行分析处理,而是先将数据写入一个有序缓冲区,然后另创建“泵”去分析处理这些数据,这样一来,不会影响数据获取环节的效率。因此,诸位可以看见有三个“泵”(数据接收,数据分析,数据处理)联合工作。

    事实上,太多“泵”协作工作也是会影响整个系统效率的,这就像多个人协同工作,虽然人多力量大,但是人多需要考虑同步共享资源、人跟人之间的协作能力等情况,这个好比“生产者消费者模式”,

图2

当“生产者-缓冲区-消费者”这一结构过多时,数据从接收到最终被处理,是需要一个漫长的过程,因此,我们需要寻找一个平衡点。有两种改进方式:

1)

图3

如上图,接收数据后,直接开启异步分析和处理过程。

2)

图4

如上图,数据接收后,将其写入缓冲区,然后另外再开启线程分析和处理数据,这个就把“数据分析”和“数据处理”合并在一块了。这两个严格来说耦合度比原来那个要高。

    注:在通讯编程中,图3适合UDP通信,因为UDP每次接受到的数据都是一个完整的数据包,数据接收后直接开始分析处理,图4适合TCP通信,因为TCP传输数据是以“流”格式传输的,并且每次接收到的数据不一定是完整的,我们必须先将接收到的数据按顺序写入一个有序的缓冲区中,然后再从缓冲区中提取完整的数据进行分析处理。

框架与客户端代码之间的关系

    总结这个的主要原因是上次在网上看见有一个人问,使用基类引用指向一个派生类实例时,为什么不能通过该引用访问派生类中使用new关键字覆盖基类的方法,而只能访问到基类中的方法。我看了他给出的实例代码,发现其实根本就没必要使用基类引用去指向派生类实例,纯属滥用。是的,好多时候我们不知道为什么要那么使用,只因为我们看见别人那样用过,代码:

 1 class People
 2 {
 3 string _name;
 4 string _sex;
 5 //
 6 public void Info()
 7 {
 8     ShowInfo();
 9 }
10 protected virtual void ShowInfo()
11 {
12     Console.WriteLine(“基本信息  姓名:”+_name+” 性别:”+_sex);
13 }
14 }
15 class Student:People
16 {
17 //
18 protected override void ShowInfo()
19 {
20     base.ShowInfo();
21     Console.WriteLine(“附加信息  职业:学生”);
22 }
23 }
24 
25 class Teacher:People
26 {
27 //
28 protected override void ShowInfo()
29 {
30     base.ShowInfo();
31     Console.WriteLine(“附加信息  职业:教师”);
32 }
33 }
View Code

以上三个类型,现在假设我要输出某一类型对象的信息,该怎么写?

public void Func(People p)

{

    p.Info();

}

这是大多数人的写法,理由很简单,它既可以输出Student的信息也可以输出Teacher的信息,确实是这样的,但是当你确定要输出信息的对象类型时(而且很多时候属于这种情况),是没必要这样写的,比如你确定要输出信息的对象类型为Student,那么你完全可以这样:

public void Func(Student s)

{

    s.Info();

}

    我真不明白为什么你明明非常确定要使用哪个类型,却偏偏要用基类引用代替派生类引用,就是因为大家常说的“依赖于抽象而非具体”吗?这个话没错,但要看场合,当你不确定要使用哪个类型时,你可以用一个抽象引用(基类引用),当你已经非常确定了使用哪个类型时,你就没必要再去使用一个抽象引用了,直接使用具体引用(派生类引用)。抽象引用能完成的东西,具体引用都能做到,反过来却不成立,如果Student类中有一个public DoHomework(),你能用People类型的引用去访问它吗?你根本不能。

    因此,可以很大胆地说,“依赖于抽象而非具体”是一个迫不得已的结论,如果编程世界里没有那么多的不确定,完全不需要这个结论,谁会去使用一个不确定性的东西呢?可是,事实上编程世界里有太多的不确定,表现最为明显的就是框架中,之所以框架中有那么多的不确定性,那是因为通常情况下,框架具有“通用性”(没有通用性的也就不叫框架了),也就是说,框架可以使用在多个场合下,而框架编写者则完全不知道每个具体场合是什么样的,有哪些功能,每个功能怎么实现的,既然不知道具体情况,那么框架编写者只有使用一系列抽象引用临时代替了。

    现在既然不确定性无可避免,那么,怎么才能让框架本身与客户端代码(框架使用者编写的代码)能够很好的“协同工作”呢?此时,我们打开我们发达的大脑,开始拼命想象,喷血联想,协同工作?好像通信中经常听到的词语,两个远程主机如果想要协同工作,双方必须遵守同一个通信协议,如下图:

图5

那么,我们完全可以把“框架”当做服务端,框架使用者编写的代码就为客户端了,他们之间协同工作也应该遵守相同的协议,如下图:

图6

具体编码中,这个协议就表现为接口(Interface)或基类(相对而言)这样的东西,框架中使用这些东西访问客户端代码,客户端代码也必须实现这些接口或者派生自这些基类。

     像框架这种依赖于抽象的做法在解决通用性的同时,还能最大限度降低耦合度,框架编写者完全不用关心使用者的具体实现,使用者只要遵守协议,怎么实现不归框架管。           当然也有缺陷,就是框架只能通过事先规定的协议去访问客户端代码,客户端代码中如果有协议之外的东西,框架是访问不到的。这就要求框架编写者在编写框架的时候考虑充分,将所有有可能涉及到的东西都归纳到协议之中。

    本篇需结合前面三篇博客(与“泵”有关的)一起阅读。希望对各位有帮助。

© 著作权归作者所有

共有 人打赏支持
IT周见智

IT周见智

粉丝 10
博文 61
码字总数 185891
作品 0
西青
springmvc+mybatis学习笔记(汇总)

springmvc+mybatis学习笔记(汇总) 标签 : springmvc mybaits [TOC] 笔记分为两大部分:mybatis和springmvc mybatis springmvc 笔记内容主要是mybatis和springmvc的一些基本概念和使用方法,...

brianway
2016/03/30
1K
2
JavaSE——MD5、16位流

声明:本栏目所使用的素材都是凯哥学堂VIP学员所写,学员有权匿名,对文章有最终解释权;凯哥学堂旨在促进VIP学员互相学习的基础上公开笔记。 MD5: 介绍:文件生成一把MD5钥匙,就是给文件产...

凯哥学堂
05/28
0
0
php 的pack方法 归纳总结

今天在弄这个pack方法,但是真不知道如何写下来,感觉很纷乱 pack--压缩资料到位字符串之中。 语法:string pack(string format, mixed [args]...); 参数一:format参数表示资料用什么方式读...

大道至簡
2012/03/28
0
0
Java程序员从笨鸟到菜鸟之——总结和声明

前段时间经过大约二十天的时间把javaSE的内容总结了一下,最近由于个人原因和一些乱七八糟的事情,一直没在更新,首先感谢那些一如既往支持我的朋友。最近不知道为什么,一直很懒,做东西也一...

长平狐
2012/11/12
64
0
系统的撸一遍MySQL - MySQL读书笔记

作为一个工作6年的PHP工程师,说实话从来没有系统的去看过一本MySQL的书,哪怕入门的书,表示真的很汗颜。。。 记得最早学数据库的时候学过sql server、oracle 但是最终没有搞Java、也没有搞...

logbird
2016/08/20
64
3
(zhuan) 深度学习全网最全学习资料汇总之模型介绍篇

This blog from : http://weibo.com/ttarticle/p/show?id=2309351000224077630868614681&u=5070353058&m=4077873754872790&cu=5070353058 深度学习全网最全学习资料汇总之模型介绍篇 雷锋网 ......

wangxiaocvpr
2017/02/22
0
0
【Visual C++】游戏开发笔记十六 讲解一个完整的回合制游戏demo

本系列文章由zhmxy555编写,转载请注明出处。 文章链接 http://blog.csdn.net/zhmxy555/article/details/7447864 作者:毛星云 邮箱: happylifemxy@qq.com 这节笔记的主要内容是介绍一个完整...

长平狐
2012/11/12
77
0
【Visual C++】游戏开发笔记十六 讲解一个完整的回合制游戏demo

本系列文章由zhmxy555编写,转载请注明出处。 文章链接 http://blog.csdn.net/zhmxy555/article/details/7447864 作者:毛星云 邮箱: happylifemxy@qq.com 这节笔记的主要内容是介绍一个完整...

长平狐
2012/11/12
202
0
RxJava 2 学习笔记 (一)

作者: 一字马胡 转载标志 【2017-12-13】 更新日志 日期 更新内容 备注 2017-12-13 RxJava2学习笔记系列 系列笔记 (一) 导入 其实在很早以前就接触过RxJava,并且当时学习RxJava还有一个产...

一字马胡
2017/12/13
0
0
jQuery

前端基础进阶:全方位详细图解面向对象、构造函数、原型与原型链 这是一篇非常适合新手理解关于面向对象一切的文章,解答了为什么我们会使用构造函数与原型,构造函数与原型的本质以及 new ...

掘金官方
01/08
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

用 Python 实现打飞机,让子弹飞吧!

所用技术和软件 python 2.7 pygame 1.9.3 pyCharm 准备工作 安装好 pygame 在第一次使用 pygame 的时候,pyCharm 会自动 install pygame。 下载好使用的素材。 技术实现 初始化 pygame 首先要...

猫咪编程
4分钟前
0
0
MySQL的行锁和表锁

简单总结一下行锁和表锁。 行锁 每次操作锁住一行数据。开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。 表锁 每次操作锁住整张表。开销小,加锁快;不会出...

to_ln
6分钟前
0
0
Java IO类库之字节数组输入流ByteArrayInputStream

一、ByteArrayInputStream字节数组输入流介绍 ByteArrayInputStream是字节数组输入流,继承自InputStream。它的内部包含一个缓冲区,是一个字节数组,缓冲数组用于保存从流中读取的字节数据,...

老韭菜
8分钟前
0
0
iOS安全应该做哪些事情

1. 尽量使用HTTPS协议。 2. 密码提交的时候,密码使用SHA256加密后传输,MD5等经过哈希碰撞已经可以推算出原文。 3. 密码提交的时候,可以加盐。 4. 密码保存在本地的时候,尽量使用钥匙串保...

HOrange
14分钟前
0
0
react native 注意事项

1. 环境参考官网 android studio 必装 java jdk安装 1.8版本(环境建议自己一步一步配置,切记不要 apt ) 2.有改变编译内容发现 会白屏,然后APP消失,请卸载原来的测试 appinfo (连续两次...

304158
20分钟前
0
0
FOMO游戏代码解析

源代码在此处

怎当她临去时秋波那一转
25分钟前
1
0
EOS智能合约与DApp开发入门

EOS的是Block.One主导研发的一个区块链底层公链系统,它专门为支撑商业去中心化 应用(Decentralized Application)而设计,其代码开源。 比特币被称为区块链1.0,因为它开辟了数字加密货币的...

笔阁
38分钟前
1
0
编译cjson到dll

https://blog.csdn.net/mengzhisuoliu/article/details/52203724 编译完成后 是纯lua实现的json decode 的10倍以上...

梦想游戏人
48分钟前
0
0
JS基础- Date 对象

Date 对象 Date 对象用于处理日期和时间。 创建 Date 对象的语法: var myDate=new Date() 注释:Date 对象会自动把当前日期和时间保存为其初始值。 Date 对象属性 属性 描述 constructor 返...

ZHAO_JH
50分钟前
0
0
Python数据分析numpy(1)

Python开源的科学计算基础库 1.表示N维数组对象ndarray 2.线性代数、傅里叶变换、随机数生成 3.广播函数,整合c++、c 一.数据的维度 1.数据 2.数据维度 3.一维数据 (1)特点 (2)Python中的...

十年磨一剑3344
52分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部