严格的说,这篇叫做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框架还是挺不错的,做大的项目可以考虑使用成熟的框架啊(当然最好是开源的哦,顺便学习),呵呵,为了保证项目周期和项目质量哈!!!!!