.net中 Timer定时器

时间:2022-12-12 23:23:14

 

作者:feiying008

      在开发一套视觉系统时,发现系统内存一直不断增加,直至系统内存爆满。一开始还以为是程序内存泄露,是图像操作算法写的有问题,但是,发现如果电机轴如果 不运行的状态下,每隔一秒进行视觉运算,发现内存增加后,但操作完会立即释放内存。而不会一直增加。这个误区耽误了我很长时间,一直以为是视觉算法的写法 有问题。但排除了这个可能后,就在想为什么加上电机轴的运转后,就内存直上不下了呢。有时经过数日的摸索,终于发现,原来罪魁祸首是.NET 的Timer定时器。因为在电机轴运行时,要时刻监控电机轴的运行状态,从而根据不同的状态做出相应的动作。而实现这个功能就是用Timer计时器。我用 的是System.Timer。这个计时器的Elapsed事件是启用的一个新的线程进行间隔操作,问题就出在这里。每次间隔触发就启用一个多线程,那多 线程的数量就不断暴增,而启用的多线程的内存回收是有.net 垃圾回收机制自动进行回收的。而这个回收时间是不确定的。所以,就导致了内存暴增的情况。嗯,就说到这里,算是一个总结。

昨天又做了一些验证,上 面是说法不正确,Elapsed事件是启用了一个线程,但此线程是从次线程启用的,所以,严格意义上来讲是不会出现线程无线增加的情况的。那暴增的内存是 从哪里来的呢?经过实验验证,如果将Timer的Interval值设定为小与100的值,则内存会不断增加,值越小,增加的越快。如果值大于100的 话,则值也会增加,但每隔一段时间都会降下去,我想应该是被.net垃圾回收机制给回收了。这才是真正的真因。

下面这篇文章很详细地讲解了定时器的知识,当然,.net 环境下一共有四种定时器。但是最优的还是下面讲解的这个。       

       在系统开发过程中经常用到定时器进行定时处理,比如比较常见的邮件群发、实时更新论坛的在线人数、文章数、点击率等。  很多情况下,我们不能对某一状态或者某一行为进行实时监控,所以就希望系统能够实现这一功能。通过多线程技术可以使得定时器的性能更高。

      尽管定时器能够自动处理或者一些批处理操作,但是定时器也给系统带来一定的安全隐患,特别是当定时进行的操作出现bug时,如果没有对Exception 做出及时的处理,系统资源将会大大的浪费,严重的情况下,可能导致系统崩溃。因此,对于定时器的使用一定要慎重,至少要保证定时处理的行为出现异常的可能 性很小,并在出现Exception的情况下及时处理。

System.Threading.Timer 是一个非常常用的定时器类,是一个使用回调方法的计时器,而且由线程池线程服务,简单且对资源要求不高。

.net中 Timer定时器public Timer (
.net中 Timer定时器    TimerCallback callback,
.net中 Timer定时器    Object state,
.net中 Timer定时器    TimeSpan dueTime,
.net中 Timer定时器    TimeSpan period
.net中 Timer定时器)
.net中 Timer定时器

参数

callback

    一个 TimerCallback 委托,表示要执行的方法。

state

    一个包含回调方法要使用的信息的对象,或者为 空引用(在 Visual Basic 中为 Nothing)。

dueTime

    TimeSpan,表示在 callback 参数调用它的方法之前延迟的时间量。指定 -1 毫秒以防止启动计时器。指定零 (0) 以立即启动计时器。

period

    在调用 callback 所引用的方法之间的时间间隔。指定 -1 毫秒可以禁用定期终止。

 方法、原理

  • 使用 TimerCallback 委托指定希望 Timer 执行的方法。计时器委托在构造计时器时指定,并且不能更改。此方法不在创建计时器的线程上执行,而是在系统提供的 ThreadPool 线程上执行。     
  • 创建计时器时,可以指定在第一次执行方法之前等待的时间量(截止时间)以及此后的执行期间等待的时间量(时间周期)。可以使用 Change 方法更改这些值或禁用计时器。     
  • 当不再需要计时器时,请使用 Dispose 方法释放计时器持有的资源。如果希望在计时器被释放时接收到信号,请使用接受 WaitHandle 的 Dispose(WaitHandle) 方法重载。计时器已被释放后,WaitHandle 便终止。     
  • 由计时器执行的回调方法应该是可重入的,因为它是在 ThreadPool 线程上调用的。

备注:

      在超过 dueTime 以后及此后每隔 period 时间间隔,都会调用一次 callback 参数所指定的委托。
      如果 dueTime 为零 (0),则立即调用 callback。如果 dueTime 是 -1 毫秒,则不会调用 callback;计时器将被禁用,但通过调用 Change 方法可以重新启用计时器。
      如果 period 为零 (0) 或 -1 毫秒,而且 dueTime 为正,则只会调用一次 callback;计时器的定期行为将被禁用,但通过使用 Change 方法可以重新启用该行为。 

最简单的定时器 

.net中 Timer定时器using System;
.net中 Timer定时器using System.Threading;
.net中 Timer定时器
.net中 Timer定时器public class TestTimer
.net中 Timer定时器{
.net中 Timer定时器    /// <summary>
.net中 Timer定时器    /// 定时器
.net中 Timer定时器    /// </summary>
.net中 Timer定时器    private Timer iTimer;
.net中 Timer定时器    /// <summary>
.net中 Timer定时器    /// constructor
.net中 Timer定时器    /// </summary>
.net中 Timer定时器    public TestTimer()
.net中 Timer定时器    {
.net中 Timer定时器        iTimer = new System.Threading.Timer(new TimerCallback(Doing));
.net中 Timer定时器        iTimer.Change(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
.net中 Timer定时器    }
.net中 Timer定时器    /// <summary>
.net中 Timer定时器    /// 
.net中 Timer定时器    /// </summary>
.net中 Timer定时器    /// <param name="nObject"></param>
.net中 Timer定时器    public void Doing(object nObject)
.net中 Timer定时器    {
.net中 Timer定时器        //do something
.net中 Timer定时器    }
.net中 Timer定时器}

 一个比较完整的计时器:

       下面是我设计的一个简单实例。在一个问卷调查系统中,每一张问卷都有其终止日期,当到达了终止日期时,需要系统自动将其关闭。这就需要定时器对问卷的状态 和终止日期进行实时监控,及时关闭。这里采用了一个简单的单件模式来操作、控制定时器。 这里主要的操作包括定时器开始、终止、执行一次。

.net中 Timer定时器
.net中 Timer定时器    /// <summary>
.net中 Timer定时器    /// 管理类
.net中 Timer定时器    /// </summary>
.net中 Timer定时器    public class PaperManager
.net中 Timer定时器    {
.net中 Timer定时器        /// <summary>
.net中 Timer定时器        /// 定时器
.net中 Timer定时器        /// </summary>
.net中 Timer定时器        private Timer iTimer;
.net中 Timer定时器        /// <summary>
.net中 Timer定时器        /// 启动时间
.net中 Timer定时器        /// </summary>
.net中 Timer定时器        private TimeSpan dueTime;
.net中 Timer定时器        /// <summary>
.net中 Timer定时器        /// 方法调用间隔
.net中 Timer定时器        /// </summary>
.net中 Timer定时器        private TimeSpan period;
.net中 Timer定时器        /// <summary>
.net中 Timer定时器        /// 委托
.net中 Timer定时器        /// </summary>
.net中 Timer定时器        private TimerCallback timerDelegate;   
.net中 Timer定时器        /// <summary>
.net中 Timer定时器        /// 静态实例
.net中 Timer定时器        /// </summary>
.net中 Timer定时器        private static readonly PaperManager self = new PaperManager();
.net中 Timer定时器        /// <summary>
.net中 Timer定时器        /// 构造函数
.net中 Timer定时器        /// </summary>
.net中 Timer定时器        public PaperManager()
.net中 Timer定时器        {
.net中 Timer定时器            timerDelegate = new TimerCallback(CheckStatus);
.net中 Timer定时器        }
.net中 Timer定时器        /// <summary>
.net中 Timer定时器        /// 
.net中 Timer定时器        /// </summary>
.net中 Timer定时器        /// <returns></returns>
.net中 Timer定时器        public static PaperManager getInstance()
.net中 Timer定时器        {
.net中 Timer定时器            return self;
.net中 Timer定时器        }
.net中 Timer定时器        /// <summary>
.net中 Timer定时器        /// 设置启动时间间隔
.net中 Timer定时器        /// </summary>
.net中 Timer定时器        /// <param name="days"></param>
.net中 Timer定时器        /// <param name="hours">小时</param>
.net中 Timer定时器        /// <param name="minutes">分钟</param>
.net中 Timer定时器        /// <param name="seconds"></param>
.net中 Timer定时器        /// <param name="milisecond">毫秒</param>
.net中 Timer定时器        public void setDueTime(int days, int hours, int minutes, int seconds, int milisecond)
.net中 Timer定时器        {
.net中 Timer定时器            dueTime = new TimeSpan(days, hours, minutes, seconds, milisecond);         }         /// <summary>         /// 设置回调时间间隔         /// </summary>         /// <param name="days">天</param>         /// <param name="hours">小时</param>         /// <param name="minutes">分钟</param>         /// <param name="seconds">秒</param>         /// <param name="milisecond">毫秒</param>         public void setPeriod(int days, int hours, int minutes, int seconds, int milisecond)         {             period = new TimeSpan(days, hours, minutes, seconds, milisecond);         }         /// <summary>         /// 开始         /// </summary>         public void Start()         {             AutoResetEvent autoEvent = new AutoResetEvent(false);             dueTime = TimeSpan.FromSeconds(0);             period = TimeSpan.FromSeconds(10);             iTimer = new Timer(timerDelegate, autoEvent, dueTime, period);             autoEvent.WaitOne(5000, false);             iTimer.Change(dueTime, period);         }         /// <summary>         /// 停止         /// </summary>         public void Stop()         {             iTimer.Dispose();         }         /// <summary>         /// 执行一次         /// </summary>         public void ExcuteOneTime()         {             if (iTimer != null)             {                 iTimer.Dispose();             }             //如果 period 为零 (0) 或 -1 毫秒,而且 dueTime 为正,则只会调用一次 callback;             //计时器的定期行为将被禁用,但通过使用 Change 方法可以重新启用该行为。             setDueTime(0, 0, 0, 0, 1);             setPeriod(0, 0, 0, 0, -1);             AutoResetEvent autoEvent = new AutoResetEvent(false);             iTimer = new Timer(timerDelegate, autoEvent, dueTime, period);             autoEvent.WaitOne(5000, false);             iTimer.Change(dueTime, period);         }         /// <summary>         /// 行为         /// </summary>         /// <param name="nObject"></param>         public void CheckStatus(object nObject)         {             AutoResetEvent autoEvent = (AutoResetEvent)nObject;             if (ExcuteUpdate())             {                                 autoEvent.Set();             }         }         /// <summary>         /// 更新         /// </summary>         /// <returns></returns>         private bool ExcuteUpdate()         {             try             {                 //应该从数据库获得Paper对象的集合,这里简略                 //List<Paper> paperList = getPaperList();                 List<Paper> paperList = new List<Paper>();                 foreach (Paper item in paperList)                 {                     if (item.EndTime <= DateTime.Now)                     {                         if (item.Status == Paper.StatusOfNormal)                         {                             item.Status = Paper.StatusOfTerminate;                         }                     }                 }                 ////执行数据更新,这里省略                 return true;             }             catch             {                 return false;             }         }         }

 这是问卷的实体类,只是简单的列出必要的属性。

.net中 Timer定时器
.net中 Timer定时器    /// <summary>
.net中 Timer定时器    /// 实体类
.net中 Timer定时器    /// </summary>
.net中 Timer定时器    public class Paper
.net中 Timer定时器    {
.net中 Timer定时器        /// <summary>
.net中 Timer定时器        /// 终止时间
.net中 Timer定时器        /// </summary>
.net中 Timer定时器        public DateTime EndTime;
.net中 Timer定时器        /// <summary>
.net中 Timer定时器        /// 状态
.net中 Timer定时器        /// </summary>
.net中 Timer定时器        public int Status;
.net中 Timer定时器        /// <summary>
.net中 Timer定时器        /// 正常
.net中 Timer定时器        /// </summary>
.net中 Timer定时器        public const int StatusOfNormal = 1;
.net中 Timer定时器        /// <summary>
.net中 Timer定时器        /// 终止
.net中 Timer定时器        /// </summary>
.net中 Timer定时器        public const int StatusOfTerminate = 2;
.net中 Timer定时器        /// <summary>
.net中 Timer定时器        /// 
.net中 Timer定时器        /// </summary>
.net中 Timer定时器        /// <param name="status"></param>
.net中 Timer定时器        /// <param name="endTime"></param>
.net中 Timer定时器        public Paper(int status, DateTime endTime)
.net中 Timer定时器        {
.net中 Timer定时器            Status = status;
.net中 Timer定时器            EndTime = endTime;
.net中 Timer定时器        }
.net中 Timer定时器    }