神奇的Timer之lock篇

摘要:
只需检查回调函数中的内容是否更改!1.每次回调都需要比较当前内容是否与之前的内容相同;此时需要将内容缓存在RichTextBox中。

严格的说,这篇叫做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对这一函数的说明吧!

神奇的Timer之lock篇第1张

神奇的Timer之lock篇第2张

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

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

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

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

免责声明:文章转载自《神奇的Timer之lock篇》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Linux 线程占用CPU过高定位分析plsql导出导入 表结构、表数据、存储过程等下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Android Handler与多线程

本文首先解释一下handler是用来干嘛的,然后通过例子介绍其在多线程中的应用。 什么是Handler      handler通俗一点讲就是用来在各个进程之间发送数据的处理对象。在任何进程中,只要获得了另一个进程的handler则可以通过 handler.sendMessage(message)方法向那个进程发送数据。基于这个机制,我们在处理多线程的时...

sass揭秘之变量

全局变量和局部变量 要了解sass或less的变量,有必要先了解下js的变量定义,先来一段js的代码: var a = 1; (function(){ a = 5; })(); console.log(a);//5 上面这段代码,匿名函数里面的a因为没有使用var关键字来定义,所以当我们在函数外打印a的时候,得到的是5,改变了一开始定义的1 var...

由浅入深TheradLocal

线程并发:在多线程并发的场景下 传递数据:我们可以通过ThreadLocal在同一线程,不同组件中传递公共变量 线程隔离:每个线程的变量都是独立的,不会相互影响 常用方法 方法声明 描述 ThreadLocal() 创建ThreadLocal对象 public void set(T value) 设置当前线程绑定的局部变量 public...

VxWorks函数栈帧剖析

VxWorks任务内存地址空间布局 VxWorks系统中,所有Tasks(包括系统任务和用户任务)都工作在特权模式,共享所有内存。A任务可以直接访问B任务的内存地址空间而不会触发任何“异常”。下图为单个任务的内存地址空间布局,任务ID(Task ID)分割任务控制块(TCB)和任务栈,任务栈中压栈函数栈帧,从高地址向低地址增长。 首先,我们查看任务列表...

一道有意思的多线程面试题 C# 代码实现

如果你对多线程的控制不怎么了解,那么理解了这篇文章的内容也许对你有帮助。鼓励先自己动手实现一遍,做不出来在看代码。 题目一:两个线程交替打印0~100的奇偶数 这道题就是说有两个线程,一个名为偶数线程,一个名为奇数线程,偶数线程只打印偶数,奇数线程只打印奇数,两个线程按顺序交替打印。本文重点不是说的这道题,这道题是下面那道题的简单版本,用来做个过渡。 效果...

C#中回滚TransactionScope的使用方法和原理

TransactionScope只要一个操作失败,它会自动回滚,Complete表示事务完成  实事上,一个错误的理解就是Complete()方法是提交事务的,这是错误的,事实上,它的作用的表示本事务完成,它一般放在try{}的结尾处,不用判断前台操作是否成功,如果不成功,它会自己回滚。 在.net 1.1的时代,还没有TransactionScope类...