• 神奇的Timer之lock篇


    严格的说,这篇叫做lock篇不是太合适,为什么这么说,看完短文就知道了!

    大家都对上一篇神奇的Timer中情景2中的示例有很多自己的看法,请允许我今天一一的评说一下吧,说的不对的地方,欢迎拍砖!

    1.还是应该写一个5分钟的定时器,只不过在回调函数中检查内容是否有变化!

    这个方案是没有问题的,因为RichTextBox中有一个Modified属性,用它可以来检查内容时候有改变,具体的代码很简单,我就不写了

    但是这个方案的不足之处在于:如果RichTextBox没有Modified属性呢?那你就需要做两件事:

    1.每次回调时需要比较当前内容与上一次内容是否相同;

    2.比较完之后,需要缓存此时RichTextBox中的内容,作为下一次比较的标准

    设想,如果内容很多的话,这样岂不是很占用空间吗,比较起来不是很耗时吗!当然,这在RichTextBox是不存在的,因为微软已经帮我们都做好了!

    2.回调函数不会被调用

    这可能是由于我文中有这么一句话:

    并且每次RichTextBox中有内容的改变,定时器就会被重置

    这句话确实有些不准确,如果按照字面意思理解,确实回调函数是不会被调用的!但是自己看我的代码,有一个needSave变量,它就是来避免这种事情的发生的!请仔细分析下我的代码吧!

    3.回调函数中应该加lock

    这点也是今天叙说的重点了,为什么要加lock呢?我想了想,主要的原因是因为System.Threading.Timer类是利用ThreadPool实现的,回调函数的线程和原始线程可能不是同一个线程,这样,两个线程中都存在对needSave变量的赋值语句,就有线程访问冲突的风险!其实仔细想想,确实需要改进一下代码,如何来改进呢?最简单的方法就是在两处对needSave变量的赋值语句都加上lock来限制,我几乎都要开始写代码了!可是一种隐隐的不爽感让我停了下来,对,就是那个lock

    说句内心话,本人非常讨厌lock语句,在平时的工作中也是尽量避免,能不用就不用,一是lock语法在累赘了,还要专门声明一个变量来供lock使用,二来,多用几个lock很容易造成程序死锁,而且还不容易发现,最后当然就是影响性能了,这也是不可避免的,这可以说是所有解决线程访问冲突方案的硬伤啊!

    于是我开始思考了!为什么要加lock呢?不就是为了解决线程访问冲突吗?那c#中解决线程访问冲突的常用方案有哪些呢?主要有下面3种:

    1.加lock:简单粗暴;

    2.互斥量,信号量等:本人认为是最佳方案,而且是整个系统范围内的,可以跨进程的哦!我下一篇准备介绍使用信号量来实现些有意思的事情!

    3.原子操作类System.Threading.Interlocked:功能有限,在一些简单的地方可以使用,有可有可能是实现上述两种方案的基础,不过有些情况下需要考虑缓存行的问题,详见下面这篇文章:

    剖析Disruptor:为什么会这么快?(二)神奇的缓存行填充

    顺便提一句,剖析Disruptor:为什么会这么快?这个系列的文章很不错,大家可以去看看!

    于是,我果断放弃了lock,考虑使用互斥量这一方案,由于每次保存操作之后都有重置信号状态这一操作,所以AutoResetEvent类是实现功能的最佳选择,于是我立刻写下了下面的代码:

     1         AutoResetEvent resetEvent = new AutoResetEvent(false);
     2         System.Threading.Timer timer = null;
     3         int dueTime = 3 * 1000;
     4         private void richTextBox1_TextChanged(object sender, EventArgs e)
     5         {
     6             if (timer == null)
     7             {
     8                 timer = new System.Threading.Timer(Callback, null, dueTime, 0);
     9             } 
    10             else if(resetEvent.WaitOne(0))
    11             {
    12                 timer.Change(dueTime, 0);
    13             }
    14         }
    15         void Callback(object state)
    16         {   
    17             Save();
    18             resetEvent.Set();
    19         }
    20         void Save()
    21         {
    22             MessageBox.Show("Save");
    23         }

    将每次保存的周期减少至3秒是为了尽快查看效果!

    设计关键点在于WaitOne函数,看看MSDN对这一函数的说明吧!

    有了MSDN的说明,我们可以很有底气的说这个代码是可以实现需求的,而且AutoResetEvent的操作都是原子操作,并发性由微软来保证,基本可以认为线程访问冲突完美的解决了!

    所以说为什么这篇叫lock篇不太合适的吧,因为根本就没有用到lock,但是使用其它更好的方案来解决了问题,呵呵,个人觉得还是挺不错的!

    代码很简单,大家Copy进自己的项目运行就可以了,源码我就不放上来了!欢迎大家继续交流!

    最后提一句,quartznet框架还是挺不错的,做大的项目可以考虑使用成熟的框架啊(当然最好是开源的哦,顺便学习),呵呵,为了保证项目周期和项目质量哈!!!!!

  • 相关阅读:
    第四章 处理器体系结构
    第四节、程序的机器语言
    第三节 信息的表示和处理
    app
    你只是看起来很努力
    tap news:week5 0.0 create react app
    28.week4
    ubuntu去除带锁文件的锁 sudo chown 用户名 目标文件夹/ -R
    26.如何使用python操作我们自己创建的docker image呢?
    25.week4 docker build 也就是创建自己的image 上传image到dockerhub 从dockerhub下载images
  • 原文地址:https://www.cnblogs.com/ILoveSleep/p/3134707.html
Copyright © 2020-2023  润新知