考试首页 | 考试用书 | 培训课程 | 模拟考场 | 考试论坛  
  当前位置:编程开发 > DotNET > C# > 文章内容
  

C教程:C中Timer使用及解决重入问题

 [ 2017年6月19日 ] 【

  下面显示一下输出结果:

  是不是感觉上面输出结果很奇怪,首先是线程1输出为1,没有问题,然后隔了2秒后线程1自增1后输出为2,这就有问题了,中间为什么还出现了线程2的输出?更奇怪的是线程2刚开始输出为1,自增1后尽然变成了3!其实这就是重入所导致的问题。别急,咱们分析一下就知道其中的缘由了。

  首先timer启动计时后,开启一个线程1执行方法,当线程1第一次输出之后,这时线程1休眠了2秒,此时timer并没有闲着,因为设置的计时间隔为1秒,当在线程1休眠了1秒后,timer又开启了线程2执行方法,线程2才不管线程1是执行中还是休眠状态,所以此时线程2的输出也为1,因为线程1还在休眠状态,并没有自增。然后又隔了1秒,这时发生同时发生两个事件,线程1过了休眠状态自增输出为2,timer同时又开启一个线程3,线程3输出的为线程1自增后的值2,又过了1秒,线程2过了休眠状态,之前的输出已经是2,所以自增后输出为3,又过了1秒……我都快晕了,大概就是这意思吧,我想表达的就是:一个Timer开启的线程处理还没有完成,到了时间,另一Timer还会继续进入该方法进行处理。

  那怎么解决这个问题呢?解决方案有三种,下面一一道来,适应不同的场景,不过还是推荐最后一种,比较安全。

  ★重入问题解决方案

  1、使用lock(Object)的方法来防止重入,表示一个Timer处理正在执行,下一个Timer发生的时候发现上一个没有执行完就等待执行,适用重入很少出现的场景(具体也没研究过,可能比较占内存吧)。

  代码跟上面差不多,在触发的方法中加入lock,这样当线程2进入触发的方法中,发现已经被锁,会等待锁中的代码处理完在执行,代码如下:

  ?

1
2
3
4
5
6
7
8
9
10
11
private static object locko = new object();
 ///
 /// System.Timers.Timer的回调方法
 ///
 ///
 ///
 private static void TimersTimerHandler(object sender, EventArgs args)
 {
 int t = ++num;
 lock (locko)
 { Console.WriteLine(string.Format("线程{0}输出:{1}, 输出时间:{2}", t, outPut.ToString(), DateTime.Now)); System.Threading.Thread.Sleep(2000); outPut++; Console.WriteLine(string.Format("线程{0}自增1后输出:{1},输出时间:{2}", t, outPut.ToString(), DateTime.Now)); } }

  执行结果:

  2、设置一个标志,表示一个Timer处理正在执行,下一个Timer发生的时候发现上一个没有执行完就放弃(注意这里是放弃,而不是等待哦,看看执行结果就明白啥意思了)执行,适用重入经常出现的场景。代码如下:

  ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private static int inTimer = 0;
///
/// System.Timers.Timer的回调方法
///
///
///
private static void TimersTimerHandler(object sender, EventArgs args)
{
int t = ++num;
if (inTimer == 0)
{
inTimer = 1;
Console.WriteLine(string.Format("线程{0}输出:{1}, 输出时间:{2}", t, outPut.ToString(), DateTime.Now));
System.Threading.Thread.Sleep(2000);
outPut++;
Console.WriteLine(string.Format("线程{0}自增1后输出:{1},输出时间:{2}", t, outPut.ToString(), DateTime.Now));
inTimer = 0;
}
}

  执行结果:

  3、在多线程下给inTimer赋值不够安全,Interlocked.Exchange提供了一种轻量级的线程安全的给对象赋值的方法(感觉比较高上大,也是比较推荐的一种方法),执行结果与方法2一样,也是放弃执行。Interlocked.Exchange用法参考这里。

  ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private static int inTimer = 0;
 ///
 /// System.Timers.Timer的回调方法
 ///
 ///
 ///
 private static void TimersTimerHandler(object sender, EventArgs args)
 {
 int t = ++num;
 if (Interlocked.Exchange(ref inTimer, 1) == 0)
 {
 Console.WriteLine(string.Format("线程{0}输出:{1}, 输出时间:{2}", t, outPut.ToString(), DateTime.Now));
 System.Threading.Thread.Sleep(2000);
 outPut++;
 Console.WriteLine(string.Format("线程{0}自增1后输出:{1},输出时间:{2}", t, outPut.ToString(), DateTime.Now));
 Interlocked.Exchange(ref inTimer, 0);
 }
 }
执行结果:

  ★总结

  终于码完字了,真心不容易啊。写博客是个挺耗精力的事情,真心佩服那些大牛们笔耕不辍,致敬!在这里稍微总结一下,timer是一个使用挺简单的类,拿来即用,这里主要总结了使用timer时重入问题的解决,以前也没思考过这个问题,解决方案也挺简单,在这里列出了三种,不知道还有没有其他的方式。这里的解决方案同时也适用多线程的重入问题。

本文纠错】【告诉好友】【打印此文】【返回顶部
将考试网添加到收藏夹 | 每次上网自动访问考试网 | 复制本页地址,传给QQ/MSN上的好友 | 申请链接 | 意见留言 TOP
关于本站  网站声明  广告服务  联系方式  站内导航  考试论坛
Copyright © 2007-2013 中华考试网(Examw.com) All Rights Reserved