.net中 Timer定時器


 

作者:feiying008

      在開發一套視覺系統時,發現系統內存一直不斷增加,直至系統內存爆滿。一開始還以為是程序內存泄露,是圖像操作算法寫的有問題,但是,發現如果電機軸如果 不運行的狀態下,每隔一秒進行視覺運算,發現內存增加后,但操作完會立即釋放內存。而不會一直增加。這個誤區耽誤了我很長時間,一直以為是視覺算法的寫法 有問題。但排除了這個可能后,就在想為什么加上電機軸的運轉后,就內存直上不下了呢。有時經過數日的摸索,終於發現,原來罪魁禍首是.NET 的Timer定時器。因為在電機軸運行時,要時刻監控電機軸的運行狀態,從而根據不同的狀態做出相應的動作。而實現這個功能就是用Timer計時器。我用 的是System.Timer。這個計時器的Elapsed事件是啟用的一個新的線程進行間隔操作,問題就出在這里。每次間隔觸發就啟用一個多線程,那多 線程的數量就不斷暴增,而啟用的多線程的內存回收是有.net 垃圾回收機制自動進行回收的。而這個回收時間是不確定的。所以,就導致了內存暴增的情況。嗯,就說到這里,算是一個總結。

昨天又做了一些驗證,上 面是說法不正確,Elapsed事件是啟用了一個線程,但此線程是從次線程啟用的,所以,嚴格意義上來講是不會出現線程無線增加的情況的。那暴增的內存是 從哪里來的呢?經過實驗驗證,如果將Timer的Interval值設定為小與100的值,則內存會不斷增加,值越小,增加的越快。如果值大於100的 話,則值也會增加,但每隔一段時間都會降下去,我想應該是被.net垃圾回收機制給回收了。這才是真正的真因。

下面這篇文章很詳細地講解了定時器的知識,當然,.net 環境下一共有四種定時器。但是最優的還是下面講解的這個。       

       在系統開發過程中經常用到定時器進行定時處理,比如比較常見的郵件群發、實時更新論壇的在線人數、文章數、點擊率等。  很多情況下,我們不能對某一狀態或者某一行為進行實時監控,所以就希望系統能夠實現這一功能。通過多線程技術可以使得定時器的性能更高。

      盡管定時器能夠自動處理或者一些批處理操作,但是定時器也給系統帶來一定的安全隱患,特別是當定時進行的操作出現bug時,如果沒有對Exception 做出及時的處理,系統資源將會大大的浪費,嚴重的情況下,可能導致系統崩潰。因此,對於定時器的使用一定要慎重,至少要保證定時處理的行為出現異常的可能 性很小,並在出現Exception的情況下及時處理。

System.Threading.Timer 是一個非常常用的定時器類,是一個使用回調方法的計時器,而且由線程池線程服務,簡單且對資源要求不高。

public Timer (
    TimerCallback callback,
    Object state,
    TimeSpan dueTime,
    TimeSpan period
)

參數

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 方法可以重新啟用該行為。 

最簡單的定時器 

using System;
using System.Threading;

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

 一個比較完整的計時器:

       下面是我設計的一個簡單實例。在一個問卷調查系統中,每一張問卷都有其終止日期,當到達了終止日期時,需要系統自動將其關閉。這就需要定時器對問卷的狀態 和終止日期進行實時監控,及時關閉。這里采用了一個簡單的單件模式來操作、控制定時器。 這里主要的操作包括定時器開始、終止、執行一次。


    /// <summary>
    /// 管理類
    /// </summary>
    public class PaperManager
    {
        /// <summary>
        /// 定時器
        /// </summary>
        private Timer iTimer;
        /// <summary>
        /// 啟動時間
        /// </summary>
        private TimeSpan dueTime;
        /// <summary>
        /// 方法調用間隔
        /// </summary>
        private TimeSpan period;
        /// <summary>
        /// 委托
        /// </summary>
        private TimerCallback timerDelegate;   
        /// <summary>
        /// 靜態實例
        /// </summary>
        private static readonly PaperManager self = new PaperManager();
        /// <summary>
        /// 構造函數
        /// </summary>
        public PaperManager()
        {
            timerDelegate = new TimerCallback(CheckStatus);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static PaperManager getInstance()
        {
            return self;
        }
        /// <summary>
        /// 設置啟動時間間隔
        /// </summary>
        /// <param name="days"></param>
        /// <param name="hours">小時</param>
        /// <param name="minutes">分鍾</param>
        /// <param name="seconds"></param>
        /// <param name="milisecond">毫秒</param>
        public void setDueTime(int days, int hours, int minutes, int seconds, int milisecond)
        {
            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;             }         }         }

 這是問卷的實體類,只是簡單的列出必要的屬性。


    /// <summary>
    /// 實體類
    /// </summary>
    public class Paper
    {
        /// <summary>
        /// 終止時間
        /// </summary>
        public DateTime EndTime;
        /// <summary>
        /// 狀態
        /// </summary>
        public int Status;
        /// <summary>
        /// 正常
        /// </summary>
        public const int StatusOfNormal = 1;
        /// <summary>
        /// 終止
        /// </summary>
        public const int StatusOfTerminate = 2;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="status"></param>
        /// <param name="endTime"></param>
        public Paper(int status, DateTime endTime)
        {
            Status = status;
            EndTime = endTime;
        }
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM