zoukankan      html  css  js  c++  java
  • DotNet中的计时器线程计时器

    转载自:http://hi.baidu.com/wingingbob/item/9f1c9615f3b24d5f2b3e225c
    
    基于多线程设计,计时器工作在ThreadPool线程上,存在事件的重入问题;
    MSDN只是说基于服务器的计时器可能比Windows计时器精确得多,具体是多少,与线程计时器的精度有关(内部由线程计时器实现),但是我们可以相信它有十分准确的1毫秒;
    通过Interval属性或者构造设定计时器触发时间,在Interval属性大于0时,并且Enabled属性为true,将引发Elapsed事件。当AutoReset属性被设置为false时,只引发一次Elapsed事件,不会周期性回调事件,其默认值为true。可以使用Start()方法和Stop()方法控制Enabled属性;
    需要使用Close方法和Dispose方法销毁资源;注意:一旦服务器计时器对象不存在任何引用,垃圾回收器会回收该对象,因此,在引用失效之前需要使用GC.KeepAlive方法使它不被回收,建议将服务器计时器声明在类级别或更高,防止此问题的发生;
    服务器计时器只能应用在.NET Framework中,不被.NET Compact Framework(掌上设备)和XNA Framework(游戏开发)支持。 System.Threading.Timer类(线程计时器) 前两个计时器(Windows计时器和服务器计时器)都继承了Component(组件)类,而且他们可以作为父类被再次继承。线程计时器则是更“轻量的”计时器,它是密封的,它的声明如下: [ComVisible(
    true), HostProtection(SecurityAction.LinkDemand, Synchronization=true, ExternalThreading=true)] public sealed class Timer : MarshalByRefObject, IDisposable { // Fields private const uint MAX_SUPPORTED_TIMEOUT = 0xfffffffe; private TimerBase timerBase; // Methods [MethodImpl(MethodImplOptions.NoInlining)] public Timer(TimerCallback callback); [MethodImpl(MethodImplOptions.NoInlining)] public Timer(TimerCallback callback, object state, int dueTime, int period); [MethodImpl(MethodImplOptions.NoInlining)] public Timer(TimerCallback callback, object state, long dueTime, long period); [MethodImpl(MethodImplOptions.NoInlining)] public Timer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period); [MethodImpl(MethodImplOptions.NoInlining), CLSCompliant(false)] public Timer(TimerCallback callback, object state, uint dueTime, uint period); public bool Change(int dueTime, int period); public bool Change(long dueTime, long period); public bool Change(TimeSpan dueTime, TimeSpan period); [CLSCompliant(false)] public bool Change(uint dueTime, uint period); public void Dispose(); public bool Dispose(WaitHandle notifyObject); private void TimerSetup(TimerCallback callback, object state, uint dueTime, uint period, ref StackCrawlMark stackMark); } 从这份声明中可以看到,它公开了五个重载的构造函数、四个重载的Change方法和两个重载的Dispose方法,甚至连一个属性都没提供,只有构造、Change、Dispose这三样东西,我们发现,越是底层的,就越简洁,而简洁的,不意味着会简单。
    构造函数 虽然有五个重载,但是第一个(上面声明代码中第10行)是不推荐的,因为它只工作在.NET框架上。这个构造函数只需要指定一个TimerCallback回调,而其他几个构造函数中都有另外三个参数。 解释一下这四个参数的意义: 第1个参数,TimerCallback回调。TimerCallback是个委托,处理计时器调用的方法。通过它使计时器到时间后执行我们写的方法,就像前两个计时器中所谓的事件,只不过这里用委托回调的方式实现的。但是要清楚,委托的方法并不是在创建Timer的线程上执行的,它会在系统提供的一个单独的线程池线程中执行。因此要在这个方法中访问创建Timer线程中的对象,需要在创建Timer的线程里再定义一个委托,同步线程后,通过这个委托去访问。 第2个参数,回调方法传递的对象。可以为null,主要看我们需要不需要有个对象。 第3个参数,启动时间。是第一次回调前延迟时间量。可以用整型数值类型,毫秒为单位,0是立即启动,Timeout.Infinite(即
    -1)是无限大,相当于禁止;也可以用TimeSpan表示时间间隔。 第4个参数,周期间隔。在第一次回调之后,周期回调需要的时间间隔。Timeout.Infinite(即-1)会禁止周期回调。同样可以用整型数值或者TimeSpan表示时间间隔。
    五个构造函数就不列出来了,看前面的。解释下第一个构造函数,它只有第一个参数,其他三个参数会被依次设定为null, Timeout.Infinite, Timeout.Infinite。
    Change方法 Change方法有四个重载,用它们可以随时修改计时器的启动时间和周期时间,它们的参数与构造函数的第3、4个参数意义相同。一个很实用的例子就是暂停计时器的周期回调: Change(
    0, Timeout.Infinite);
    官方文档中:
    
    

    如果 dueTime 是零 (0),會立即叫用回呼方法。 如果 dueTime 是 Infinite,則永不叫用回呼方法;會停用計時器,若要重新啟用,請呼叫 Change 並為 dueTime 指定正值。

    
    

    如果 period 是零 (0) 或 Infinite,而且 dueTime 不是 Infinite,則只叫用回呼方法一次;會停用計時器的定期行為,若要重新啟用,請呼叫 Change 並為 period 指定正值。

    Dispose方法 两个Dispose方法,都是用来释放该计时器使用的资源。在流程计时器使用完之后,一定记得执行该方法销毁资源。需要解释一下它的重载Dispose(WaitHandle),它只被.NET框架平台支持。它的作用是在释放完计时器的时候发出WaitHandle信号,而且在资源释放成功后会返回true。WaitHandle是个抽象类,而我们通常选择继承了它的AutoResetEvent类的对象来发送信号。有信号的好处是我们可以给销毁计时器预留一些时间,来等待计时器占用的资源被全部释放完之后再执行其他代码。 就目前来看,你只要会用AutoResetEvent类的Set方法和WaitOne方法就足够读懂下面的例子了。
    虽然这个例子并没有演示带信号的Dispose的方法,但是它(MSDN)巧妙地利用AutoResetEvent对象作为计时器委托的参数与Main的线程交互,请认真阅读下面的每行代码和每句注释,你同时会掌握线程计时器和AutoResetEvent类的使用。 [例1]

    using System;   
    using System.Threading;   
      
    class TimerExample   
    {   
        static void Main()   
         {   
            // 将会作为参数传入计时器回调的方法中,我们通过它向Main函数发送信号。   
             AutoResetEvent autoEvent = new AutoResetEvent(false);   
            // StatusChecker是我们写的包含回调方法,进行状态检查的类。假设要检查5次。   
             StatusChecker   statusChecker = new StatusChecker(5);   
            // 为计时器创建一个用于请求statusChecker.CheckStatus方法的委托   
             TimerCallback timerDelegate = new TimerCallback(statusChecker.CheckStatus);   
               
             Console.WriteLine("{0} 创建计时器。
    ", DateTime.Now.ToString("H:mm:ss.fff"));   
            // 创建计时器,用autoEvent作为委托方法的参数,计时器启动时间是1秒,周期间隔250毫秒,   
            // 也就是1秒后请求CheckStatus方法,之后每250毫秒请求一次。   
             Timer stateTimer = new Timer(timerDelegate, autoEvent, 1000, 250);   
               
            // 在5秒内等待autoEvent信号   
             autoEvent.WaitOne(5000, false);   
            // 收到autoEvent信号后或者超过5秒钟的等待,改变计时周期间隔为500毫秒   
            // 由于计时器已经启动,启动时间设置为0就可以了。   
             stateTimer.Change(0, 500);   
             Console.WriteLine("
    改变计时周期间隔。
    ");   
               
            // 在5秒内等待autoEvent信号   
             autoEvent.WaitOne(5000, false);   
            // 在第二次收到autoEvent信号或者超过5秒钟,销毁计时器。   
             stateTimer.Dispose();   
             Console.WriteLine("
    销毁计时器。");   
         }   
    }   
      
    class StatusChecker   
    {   
        int invokeCount, maxCount;   
      
        public StatusChecker(int count)   
         {   
             invokeCount   = 0;   
             maxCount = count; //检查次数   
         }   
      
        // 被计时器委托调用的方法   
        public void CheckStatus(Object stateInfo)   
         {   
             AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;   
             Console.WriteLine("{0} 状态检查 {1,2}",   
                 DateTime.Now.ToString("H:mm:ss.fff"), (++invokeCount).ToString());   
            if(invokeCount == maxCount)   
             {   
                // 计数清零,然后向Main函数发送信号。   
                 invokeCount   = 0;   
                 autoEvent.Set();   
             }   
         }   
    }  
    using System;   
    using System.Text;   
    using System.Drawing;   
    using System.Windows.Forms;   
    using System.ComponentModel;   
      
    namespace MessageWindow   
    {   
        public partial class MessageWindow : Form   
         {   
            //声明一个线程计时器   
             System.Threading.Timer _timer;   
            //窗口自动关闭倒计时,如果没在构造时更改,默认为不关闭。   
            int _interval = System.Threading.Timeout.Infinite;   
            //关闭窗口的委托,其他线程通过这个委托来调用关闭当前窗口的代码。   
            delegate void CloseDelegate();   
      
            private MessageWindow()   
             {   
                 InitializeComponent();   
                 picIcon.Image = System.Drawing.SystemIcons.Information.ToBitmap();   
                //实例化计时器,回调方法到TimerTick,无参数,不启动,禁止周期回调。   
                 _timer = new System.Threading.Timer(   
                    new System.Threading.TimerCallback(TimerTick),   
                    null,   
                     System.Threading.Timeout.Infinite,   
                     System.Threading.Timeout.Infinite);   
                //获得主屏幕的工作区矩形   
                 Rectangle workArea = Screen.PrimaryScreen.WorkingArea;   
                //将窗口显示在屏幕右下方位置   
                 StartPosition = FormStartPosition.Manual;   
                 Location = new Point(workArea.Right - this.Width, workArea.Bottom - this.Height);   
             }   
      
            public MessageWindow(string caption, string text)   
                 : this()   
             {   
                 lblCaption.Text = caption;   
                 lblText.Text = text;   
             }   
      
            /// <summary>   
            /// 消息窗口   
            /// </summary>   
            /// <param name="caption">消息标题</param>   
            /// <param name="text">消息内容</param>   
            /// <param name="interval">消息窗口自动消失时间</param>   
            public MessageWindow(string caption, string text, int interval)   
                 : this(caption, text)   
             {   
                 _interval = interval;   
             }   
      
            protected override void OnLoad(EventArgs e)   
             {   
                //动画渐入   
                 NativeMethods.AnimateWindow(this.Handle, 500, NativeConstants.AW_BLEND + NativeConstants.AW_ACTIVATE);   
                //用_interval启动计时器,不进行周期计时。   
                 _timer.Change(_interval, System.Threading.Timeout.Infinite);   
                base.OnLoad(e);   
             }   
      
            //计时器回调方法。这里的代码将在线程池线程上执行,调用UI线程窗口的Close方法需要请求线程同步,   
            //通过UI线程的CloseDelegate委托执行关闭窗口,用窗口的Invoke方法执行这个被委托的代码。   
            void TimerTick(object obj)   
             {   
                if (this.InvokeRequired)   
                 {   
                     CloseDelegate closeme = delegate  
                     {   
                        this.Close();   
                     };   
                    this.Invoke(closeme);   
                 }   
             }   
      
            //在窗口关闭时销毁线程计时器资源,动画渐出。   
            protected override void OnFormClosed(FormClosedEventArgs e)   
             {   
                if (_timer != null) _timer.Dispose();   
                 NativeMethods.AnimateWindow(this.Handle, 500, NativeConstants.AW_BLEND + NativeConstants.AW_HIDE);   
                base.OnFormClosed(e);   
             }   
         }   
    }
  • 相关阅读:
    Android连载7-动语添加碎片
    JavaScript连载6-转化为Number和Boolean类型、运算符
    Java连载111-timer定时器、反射机制概述
    用conda创建虚拟环境的一些常用命令
    Java内存分析
    Java语言中的Class类
    线程协作
    LeetCode刷题笔记第26题
    LeetCode刷题笔记第20题(括号匹配)
    LeetCode刷提笔记第1332题
  • 原文地址:https://www.cnblogs.com/wucaifang/p/3680445.html
Copyright © 2011-2022 走看看