自己開發能在asp.net項目正常使用的定時器WebTimer,讓定時器聽話起來


簡述:

iis是一個很不錯的服務器,有很多很好用的特性來支持網站運行,但有時候這些特性卻會影響到我們開發者的一些操作。比如我們需要定時運行做一些操作,但由於iis的利用應用程序池來管理這種方式會讓網站所在的進程在空閑(一段時間沒人訪問)時注銷該線程,所以定時器是無法正常運行的。但利用asp.net的Cache機制是可以巧妙的實現能正常使用的定時器的。

運行效果:

請打開該鏈接

該網站定時每一個小時給該網頁添加一條記錄,而且估計我這個站點幾百年都不會有一個人來訪問一次的,大部分時間都是處於空閑狀態的。

怎么使用:

網站添加WebTimerLib類庫的引用

在合適的位置編寫以下一行代碼

 WebTimerLib.WebTimer timer = new WebTimerLib.WebTimer(new System.Threading.TimerCallback(TimeCallback), null, TimeSpan.FromSeconds(2), TimeSpan.FromHours(1));

 或者以下一行代碼,推薦這行

WebTimerLib.WebTimer timer = new WebTimerLib.WebTimer(new System.Threading.TimerCallback(TimeCallback), null, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(60), "20130728");

 

 

這行代碼表示2秒后沒一個小時運行一次TimeCallback這個方法

具體每個參數會在源碼里有注釋

源碼:

源碼是在.net 3.5的環境下開發的,估計.net2.0也是可以編譯通過的。

類庫下載地址

WebTimer類的代碼: 

 

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Text;
  4 using System.Threading;
  5 using System.Web.Caching;
  6 using System.Web;
  7 
  8 namespace WebTimerLib
  9 {
 10     /// <summary>
 11     /// 可以正常在web使用的定時器
 12     /// </summary>
 13     public class WebTimer : IDisposable
 14     {
 15         /// <summary>
 16         /// 初始化 Timer 類的新實例,使用 TimeSpan 值來度量時間間隔。
 17         /// </summary>
 18         /// <param name="callback">一個 TimerCallback 委托,表示要執行的方法。 </param>
 19         /// <param name="state">一個包含回調方法要使用的信息的對象,或者為 空引用(在 Visual Basic 中為 Nothing)。 </param>
 20         /// <param name="dueTime">TimeSpan,表示在 callback 參數調用它的方法之前延遲的時間量。指定 -1 毫秒以防止啟動計時器。指定零 (0) 以立即啟動計時器。 </param>
 21         /// <param name="period">在調用 callback 所引用的方法之間的時間間隔。指定 -1 毫秒可以禁用定期終止。 </param>
 22         /// <param name="timerID">定時器標識符,不能重復</param>
 23         /// <remarks>
 24         /// 如果 dueTime 為零 (0),則立即調用 callback。如果 dueTime 是 -1 毫秒,則不會調用 callback;計時器將被禁用,但通過調用 Change 方法可以重新啟用計時器。
 25         /// 如果 period 為零 (0) 或 -1 毫秒,而且 dueTime 為正,則只會調用一次 callback;計時器的定期行為將被禁用,但通過使用 Change 方法可以重新啟用該行為。
 26         /// 為 callback 指定的方法應是可重入的,這是因為 ThreadPool 線程會調用該方法。該方法在以下兩種情況下可以同時在兩個線程池線程中執行:一是計時器間隔小於執行該方法所需的時間;二是所有線程池線程都在使用,並且多次對該方法進行排隊。
 27         /// </remarks>
 28         public WebTimer(TimerCallback callback, Object state, TimeSpan dueTime, TimeSpan period, string timerID)
 29         {
 30             _callback = callback;
 31             _state = state;
 32             _dueTime = dueTime;
 33             _period = period;
 34             _cacheID = timerID;
 35             Run(dueTime);
 36         }
 37 
 38         /// <summary>
 39         /// 初始化 Timer 類的新實例,使用 TimeSpan 值來度量時間間隔, 建議使用WebTimer(TimerCallback callback, Object state, TimeSpan dueTime, TimeSpan period, string timerID),避免造成更改定時器操作的時候,舊操作仍舊在運行
 40         /// </summary>
 41         /// <param name="callback">一個 TimerCallback 委托,表示要執行的方法。 </param>
 42         /// <param name="state">一個包含回調方法要使用的信息的對象,或者為 空引用(在 Visual Basic 中為 Nothing)。 </param>
 43         /// <param name="dueTime">TimeSpan,表示在 callback 參數調用它的方法之前延遲的時間量。指定 -1 毫秒以防止啟動計時器。指定零 (0) 以立即啟動計時器。 </param>
 44         /// <param name="period">在調用 callback 所引用的方法之間的時間間隔。指定 -1 毫秒可以禁用定期終止。 </param>
 45         /// <param name="timerID">定時器標識符,重復的話會取消之前的任務</param>
 46         /// <remarks>
 47         /// 如果 dueTime 為零 (0),則立即調用 callback。如果 dueTime 是 -1 毫秒,則不會調用 callback;計時器將被禁用,但通過調用 Change 方法可以重新啟用計時器。
 48         /// 如果 period 為零 (0) 或 -1 毫秒,而且 dueTime 為正,則只會調用一次 callback;計時器的定期行為將被禁用,但通過使用 Change 方法可以重新啟用該行為。
 49         /// 為 callback 指定的方法應是可重入的,這是因為 ThreadPool 線程會調用該方法。該方法在以下兩種情況下可以同時在兩個線程池線程中執行:一是計時器間隔小於執行該方法所需的時間;二是所有線程池線程都在使用,並且多次對該方法進行排隊。
 50         /// </remarks>
 51         public WebTimer(TimerCallback callback, Object state, TimeSpan dueTime, TimeSpan period)
 52         {
 53             _callback = callback;
 54             _state = state;
 55             _dueTime = dueTime;
 56             _period = period;
 57             Run(dueTime);
 58         }
 59 
 60         private TimerCallback _callback;
 61         private object _state;
 62         private TimeSpan _dueTime;
 63         private TimeSpan _period;
 64         private bool _isDisposing = false;
 65         private DateTime _NewDoCallbackTime;
 66 
 67         protected void Run(TimeSpan dueTime)
 68         {
 69             _NewDoCallbackTime = DateTime.Now.Add(dueTime);
 70             HttpRuntime.Cache.Insert(CacheID, this, null, _NewDoCallbackTime, Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, new CacheItemRemovedCallback(CacheItemRemovedCallback));
 71 
 72         }
 73 
 74         private void CacheItemRemovedCallback(String key, Object value, CacheItemRemovedReason reason)
 75         {
 76             WebTimer timer = (WebTimer)value;
 77             if (timer._isDisposing)
 78                 return;
 79             if (reason == CacheItemRemovedReason.Expired)
 80             {
 81                 if (timer._period < TimeSpan.FromSeconds(0))
 82                     _isDisposing = true;
 83                 timer._callback(_state);
 84                 timer.Run(timer._period);
 85             }
 86             else
 87             {
 88                 timer.Run(_NewDoCallbackTime - DateTime.Now);
 89             }
 90         }
 91 
 92         /// <summary>
 93         /// 更改計時器的啟動時間和方法調用之間的時間間隔,使用 TimeSpan 值度量時間間隔。 
 94         /// </summary>
 95         /// <param name="dueTime">一個 TimeSpan,表示在調用構造 Timer 時指定的回調方法之前的延遲時間量。指定負 -1 毫秒以防止計時器重新啟動。指定零 (0) 以立即重新啟動計時器。</param>
 96         /// <param name="period">在構造 Timer 時指定的回調方法調用之間的時間間隔。指定 -1 毫秒可以禁用定期終止。 </param>
 97         /// <returns></returns>
 98         public bool Change(TimeSpan dueTime, TimeSpan period)
 99         {
100             this._dueTime = dueTime;
101             this._period = period;
102             try
103             {
104                 HttpRuntime.Cache.Remove(CacheID);
105                 this.Run(this._dueTime);
106                 return true;
107             }
108             catch (Exception)
109             {
110                 return false;
111             }
112         }
113 
114         private string _cacheID;
115         protected string CacheID
116         {
117             get
118             {
119                 if (_cacheID == null) _cacheID = GetHashCode().ToString();
120                 return _cacheID;
121             }
122         }
123 
124         #region IDisposable 成員
125 
126         public void Dispose()
127         {
128             _isDisposing = true;
129         }
130 
131 
132 
133         #endregion
134 
135     }
136 }

 

補充:

對於我的書寫能力我表示歉意,小時候語文沒學好,文筆不好請見諒;能力水平有限,有什么說錯的地方請多多指教;

關於轉載,我不喜歡別人轉載我的文章,覺得有價值的話就收藏到筆記之類軟件,不會給搜索引擎收錄的地方,我不太關注我的文章的版權問題,但我實在不喜歡自己在網上搜索資料的時候,搜到滿滿的一頁是自己曾寫過的一篇文章,實在惡心到我自己,我就注意到了一個情況,很多轉載都不是為了學習的,而是一些公司不知道基於什么想法就轉了過去,這樣造成了搜索自己想要的資源會越來越難。但我發現如果是搜英文的資料的時候就很少有重復的,o(︶︿︶)o 唉,實在糾結啊!

補充(2013-7-28 7:08):

喉嚨發炎,所以喝水太多,於是半夜起床,看了下定時器的運行情況,發現了奇怪的情況,如下圖

 

有問題,怎么能安心入眠呢?於是一個經典的場面出現了,一個酷斃(苦逼)的程序猿在夜深人靜的時候努力的敲着代碼。

問題在那里呢?認真看運行結果,是不是發現好像有兩個定時器在運行呢?沒錯,就是有兩個定時器在運行了。

原來是我的運行結果用了Cache來保存,運行的站點是個免費的國外空間,估計我們半夜的時候是人家那里烈陽當頭的時候,所以出現了內存緊張,我存着結果的Cache給回收了。Cache給回收之后,下次有人訪問的時候就會重新設置一個定時器。

於是發現了一個題外話,有個哥們在1:51分的時候來關顧過我的網站,哦,這個網站運行環境的時候估計和我們有12個小時的時差。

問題描述出來了,那接下來就是解決

小弟不用Cache了,直接存在一個文本文件里。

於是問題解決了,並且我改了定時器的運行周期,半小時一次,然后我們繼續等待測試結果吧。

最后,我對代碼做了點修改,修改的范圍在53到58行之間,之前有使用的孩紙,就重新復制粘貼下吧,沒有的話就不用管這句話了

更新說明(2013-07-28 9:33)

重載一個構造函數

WebTimer(TimerCallback callback, Object state, TimeSpan dueTime, TimeSpan period, string timerID)


免責聲明!

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



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