簡述:
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)
