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

posted @   ILoveSleep  阅读(1878)  评论(0编辑  收藏  举报
编辑推荐:
· MySQL 优化利器 SHOW PROFILE 的实现原理
· 在.NET Core中使用异步多线程高效率的处理大量数据
· 聊一聊 C#前台线程 如何阻塞程序退出
· 几种数据库优化技巧
· 聊一聊坑人的 C# MySql.Data SDK
阅读排行:
· 干掉EasyExcel!FastExcel初体验
· .NET 阻止系统睡眠/息屏
· 快手后端面试,被面试官秒挂了!
· .NET 9 中的 多级缓存 HybridCache
· 跟着 8.6k Star 的开源数据库,搞 RAG!
点击右上角即可分享
微信分享提示