• 分析System.Windows.Forms.Timer和另两个Timer的重入行为


    不熟悉Timer的可以先看看MSDN的描述:计时器  服务器计时器、Windows 计时器和线程计时器
    简单来讲:
    System.Windows.Forms.Timer基于Windows消息循环,用事件方式触发,在界面线程执行;
    System.Timers.Timer更精确,用事件方式触发,在线程池执行;
    System.Threading.Timer设计为非常轻量级,用回调函数引发,在线程池执行。
    概念约定:在一次执行未结束时,到了第二次执行的时间,如果第二次不等第一次结束便马上执行,则称为重入
    由于使用多线程,System.Timers.Timer和System.Threading.Timer是会重入的。
    那么可以想象:
    1.计算时间的线程应该不是执行用户函数的线程;
    2.执行线程不会总是同一个线程。
    下面重点讨论System.Windows.Forms.Timer(下面简称WinTimer)。
    由于WinTimer是基于Windows消息循环,显然是为WinForm程序准备,所以WinTimer的引发都在界面线程。
    那么理论上讲,如果界面线程阻塞,则不可能收到WinTimer的事件。

    看代码:

    public class Form1 : Form
    {
        Timer timer 
    = new Timer();
        
    int num = 0;

        
    public Form1()
        {
            timer.Interval 
    = 100;
            timer.Tick 
    += new EventHandler(timer_Tick);
            timer.Start();
        }

        
    void timer_Tick(object sender, EventArgs e)
        {
            num
    ++;
            
    switch (num)
            {
                
    case 1// 第一次
                    System.Threading.Thread.Sleep(3000);
                    Console.Write(num);
                    
    break;

                
    case 2// 第二次
                    timer.Stop();
                    Console.Write(num);
                    
    break;
            }
        }
    }
    是的,输出窗口(“调试”->“窗口”->“输出”)显示为:12
    那么,下面的代码,你认为会怎么输出?

    public class Form1 : Form
    {
        Timer timer 
    = new Timer();
        
    int num = 0;

        
    public Form1()
        {
            timer.Interval 
    = 100;
            timer.Tick 
    += new EventHandler(timer_Tick);
            timer.Start();
        }

        
    void timer_Tick(object sender, EventArgs e)
        {
            num
    ++;
            
    int temp = num;
            
    switch (num)
            {
                
    case 1// 第一次
                    MessageBox.Show(num.ToString());
                    Console.Write(temp);
                    
    break;

                
    case 2// 第二次
                    timer.Stop();
                    Console.Write(temp);
                    
    break;
            }
        }
    }
    输出:12?
    不见得!去运行它,你会发现,当你看到弹出窗口还没来得及点击的时候,输出窗口就已经输出2
    当你点击弹出窗口的“确定”按钮,输出窗口会输出1。
    也就是说,顺序是:21!
    于是我们惊讶的发现:Thread.Sleep可以阻止WinTimer的事件再次触发,而MessageBox.Show却无法阻止!
    万事皆有其因!对程序来讲尤其如此。

    分析:
    之所以Thread.Sleep阻止了WinTimer,是因为它阻塞了界面线程。
    而MessageBox.Show呢?它也阻塞了界面啊!
    从程序运行过程分析,MessageBox.Show必须要等我们点击了“确定”按钮才会执行下一句代码,通过逐句跟踪我们发现程序确实停在了这里。
    而case 2里的Console.Write为什么在这中间还能得到运行呢?
    将case 2里的Console.Write行代码设置断点。
    运行。
    断点被命中的时候,打开调用堆栈窗口(“调试”->“窗口”->“调用堆栈”),你会看到两个timer_Tick方法,双击下面那个timer_Tick,你会看到:

    原来第二次timer事件在Message.Show方法里!(有兴趣的可以打开“选项”->“调试”->勾掉“启用'仅我的代码'”,看内部方法)
    真相大白:Message.Show虽然阻塞了外面的代码,但它里面仍然有消息循环,于是Timer的事件得以触发。
    结论:当没有消息循环或被阻塞的时候,WinTimer的事件是触发不了的,但此线程只要有消息循环,WinTimer就可以正常触发。
    Thread.Sleep以及任何无消息循环的代码,都可以阻塞消息循环,也便阻塞了WinTimer;
    而Message.Show,Form.ShowDialog等,它们虽然阻塞了外面的消息循环,但它们里面也有消息循环,所以不会阻塞WinTimer。

    附:既然System.Windows.Forms.Timer是基于消息循环,那么当然只要有消息循环就可以使用System.Windows.Forms.Timer,
    那么WebForm添加System.Windows.Forms引用,也是可以这样使用System.Windows.Forms.Timer的:
    Code
    我可没推荐你真的这么用!

    转载需注明出处:http://cnblogs.com/zhucai/

    我的微博:http://weibo.com/zhucai

  • 相关阅读:
    TPS限流
    JDK并发基础与部分源码解读
    tomcat6-servlet规范对接 与 ClassLoader隔离
    tomcat6-输入输出buffer设计
    tomcat6-endpoint设计
    springMVC请求路径 与实际资源路径关系
    mysql 常用的数据类型
    认识IPv4分组
    CSMA/CD协议(载波侦听多路访问/碰撞检测) 最小帧长理解
    简单的vector--- 2
  • 原文地址:https://www.cnblogs.com/zhucai/p/timer.html
Copyright © 2020-2023  润新知