• .net的Timer


          .NET 提供了三种Timer:

    基于服务的Timer   System.Timers.Timer
    基于线程的Timer   System.Threading.Timer 
    基于窗体的Timer   System.Windows.Forms.Timer

         1、System.Threading.Timer,希望在另一个线程上定时执行后台任务时,这个定时器是最好的定时器。

         2、System.Windows.Forms.Timer。构造该类的实例可以告诉windows将定时器和调用线程相关联。随着定时器的触发,Windows将一个定时器消息(WM_TIMER)插入到线程的消息队列中,调用线程必须执行一个消息泵(message pump),从而提取消息,并将它们分配到期望的回调方法中。所有这些工作都是由一个线程完成的,设置定时器的线程保证时执行回调方法的线程。这就意味着定时器方法不能别多个线程同时调用。

         3、System.Timers.Timer。这个定时器基本上是对Threading的Timer类的包装,当定时器时间到期后,将导致CLR将事件加入线程池的队列。此类派生自Sys.ComponentModel的Component类,Component类允许将这些定时器放到VS设计界面上。因此如果希望在设计界面上使用定时器,可以使用这个。一般情况下,还是使用Threading的Timer类。

    其中Windows Timers只是提供了和WinAPI 一样的Timer,仍然是基于消息,是单线程的。其它两个是基于线程池的Thread Pool,这样产生的时间间隔准确均匀。

    单线程Timer问题

      Win32 API中有个SetTimer函数,可以为一个窗口创建一个定时器,这个定时器会定时产生消息WM_TIMER也可以调用指定的回调函数,但是是单线程的。

    单线程的定时器会有很多问题,首先是不准时,定时器只是定时把消息WM_TIMER访到线程的消息队列里,但是并不保证消息会立刻被响应,如果碰巧系统比较忙,那么消息可能会在队列里放一端时间才被响应,还会造成本来应该间隔一段时间发生的消息响应连续发生。

    解决方法通常是

    OnTimer(...)
    {
     //Timer process.....
     
     MSG msg;
     While(PeekMessage(&msg, m_hWnd, WM_TIMER, WM_TIMER, PM_REMOVE));
    }
    在当前Timer处理中,把消息队列里的WM_TIMER消息,清除掉。但是如果你不去调用GetMessage,那么就不会有Timer发生了。
     多线程Timer重入问题

    由于使用多线程定时器,就会出现如果一个Timer处理没有完成,到了时间下一个照样会发生,这就会导致重入。

    对付重入问题通常的办法是加锁,但是对于 Timer却不能简单的这样做,你需要评估一下。

    首先Timer处理里本来就不应该做太需要时间的事情,或者花费时间无法估计的事情,比同远方的服务器建立一个网络连接,这样的做法尽量避免。

    如果实在无法避免,那么要评估Timer处理超时是否经常发生,如果是很少出现,那么可以用lock(Object)的方法来防止重入。
    如果这种情况经常出现呢?那就要用另外的方法来防止重入。

    可以设置一个标志,表示一个Timer处理正在执行,下一个Timer发生的时候发现上一个没有执行完就放弃执行。

    static  int Finished= 0;
    public static void threadTimerCallback(Object obj)
    {
         if ( Finished== 0 )
        {
             Finished= 1;         

             Thread.Sleep(2000);

             Finished= 0;
          }
    }

    但是在多线程下给inTimer赋值不够安全。

    Interlocked.Exchange提供了一种轻量级的线程安全的给对象赋值的方法。

     static int Finished= 0;
     public static void threadTimerCallback(Object obj)
     {
           if ( Interlocked.Exchange(ref Finished, 1) == 0 )
          {
               Thread.Sleep(250);

               Interlocked.Exchange(ref Finished, 0);
          }
     }

  • 相关阅读:
    网络编程 Linux IP地址、子网掩码、网关设置和获取
    RFC 3021 Using 31Bit Prefixes on IPv4 PointtoPoint Links
    MySQL SSL 加密连接浅析
    四两拨千斤 —— Ubuntu kernel eBPF 0day分析[腾讯安全应急响应中心20180408]
    supervisor中group与program同时存在,部分program在gunicorn中不启动
    阅读习惯
    网络对抗技术Exp2后门原理与实践
    攻击树测试
    密码常识测试
    网络对抗技术Exp1逆向破解实验
  • 原文地址:https://www.cnblogs.com/jyz/p/1300428.html
Copyright © 2020-2023  润新知