C#中關(guān)于Timer定時(shí)器重入問題如何解決?這個(gè)問題可能是我們?nèi)粘W(xué)習(xí)或工作經(jīng)常見到的。希望通過這個(gè)問題能讓你收獲頗深。下面是小編給大家?guī)淼膮⒖純?nèi)容,讓我們一起來看看吧!
成都創(chuàng)新互聯(lián)公司堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都網(wǎng)站建設(shè)、做網(wǎng)站、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的普洱網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!
項(xiàng)目中用到了定時(shí)器隨著服務(wù)啟動(dòng)作定時(shí)任務(wù),按指定的準(zhǔn)點(diǎn)時(shí)間定時(shí)執(zhí)行相關(guān)操作,但是在指定準(zhǔn)點(diǎn)時(shí)間內(nèi)我只想讓它執(zhí)行一次,要避免重入問題的發(fā)生。
首先簡單介紹一下timer,這里所說的timer是指的System.Timers.timer,顧名思義,就是可以在指定的間隔是引發(fā)事件。官方介紹在這里,摘抄如下:
Timer 組件是基于服務(wù)器的計(jì)時(shí)器,它使您能夠指定在應(yīng)用程序中引發(fā) Elapsed 事件的周期性間隔。然后可通過處理這個(gè)事件來提供常規(guī)處理。 例如,假設(shè)您有一臺關(guān)鍵性服務(wù)器,必須每周 7 天、每天 24 小時(shí)都保持運(yùn)行。 可以創(chuàng)建一個(gè)使用 Timer 的服務(wù),以定期檢查服務(wù)器并確保系統(tǒng)開啟并在運(yùn)行。 如果系統(tǒng)不響應(yīng),則該服務(wù)可以嘗試重新啟動(dòng)服務(wù)器或通知管理員。 基于服務(wù)器的 Timer 是為在多線程環(huán)境中用于輔助線程而設(shè)計(jì)的。 服務(wù)器計(jì)時(shí)器可以在線程間移動(dòng)來處理引發(fā)的 Elapsed 事件,這樣就可以比 Windows 計(jì)時(shí)器更精確地按時(shí)引發(fā)事件。
那使用這個(gè)計(jì)時(shí)器有啥好處呢?主要因?yàn)樗峭ㄟ^.NET Thread Pool實(shí)現(xiàn)的、輕量、計(jì)時(shí)精確、對應(yīng)用程序及消息沒有特別的要求。
Timer是怎么使用的之前有寫過這篇:C# System.Timers.Timer定時(shí)器的使用和定時(shí)自動(dòng)清理內(nèi)存應(yīng)用
什么叫重入呢?這是一個(gè)有關(guān)多線程編程的概念:程序中,多個(gè)線程同時(shí)運(yùn)行時(shí),就可能發(fā)生同一個(gè)方法被多個(gè)進(jìn)程同時(shí)調(diào)用的情況。當(dāng)這個(gè)方法中存在一些非線程安全的代碼時(shí),方法重入會(huì)導(dǎo)致數(shù)據(jù)不一致的情況。Timer方法重入是指使用多線程計(jì)時(shí)器,一個(gè)Timer處理還沒有完成,到了時(shí)間,另一Timer還會(huì)繼續(xù)進(jìn)入該方法進(jìn)行處理。
關(guān)于定時(shí)器的重入問題解決方法嘗試如下:
1、使用鎖lock(Object)的方法來防止重入,表示一個(gè)Timer處理正在執(zhí)行,下一個(gè)Timer發(fā)生的時(shí)候發(fā)現(xiàn)上一個(gè)沒有執(zhí)行完就等待執(zhí)行,適用重入很少出現(xiàn)的場景。在觸發(fā)的方法中加入lock,這樣當(dāng)線程2進(jìn)入觸發(fā)的方法中,發(fā)現(xiàn)已經(jīng)被鎖,會(huì)等待鎖中的代碼處理完在執(zhí)行,代碼如下:
private static System.Timers.Timer aTimer = new System.Timers.Timer(); private static object loker=new object(); /// <summary> /// 設(shè)置定時(shí)器 /// </summary> public static void SetTimer() { //讀取配置時(shí)間 try { aTimer.Interval = 30000; aTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnTimedEvent); aTimer.AutoReset = true;//每到指定時(shí)間Elapsed事件是到時(shí)間就觸發(fā) aTimer.Enabled = true; //指示 Timer 是否應(yīng)引發(fā) Elapsed 事件。 } catch (Exception ex) { LogManager.RecordLog(LogType.Error, "ipad數(shù)據(jù)同步出錯(cuò):" +ex.Message,ex); } } private static void OnTimedEvent(Object source, ElapsedEventArgs e) { //如果當(dāng)前時(shí)間是為配置的準(zhǔn)點(diǎn)時(shí)間就進(jìn)來 var time =Convert.ToInt32( SynchronousHelper.AppConfig.get_Items("SycTime")); if (DateTime.Now.Hour == time && DateTime.Now.Minute == 0) { //lock,這樣當(dāng)線程2進(jìn)入觸發(fā)的方法中,發(fā)現(xiàn)已經(jīng)被鎖,會(huì)等待鎖中的代碼處理完在執(zhí)行 lock (loker) { LogManager.RecordLog(LogType.Info, "數(shù)據(jù)開始同步時(shí)間:" + e.SignalTime, null); SetTimerStart(); System.Threading.Thread.Sleep(60000); //執(zhí)行完越過當(dāng)前分鐘,使整點(diǎn)內(nèi)只能進(jìn)來一次 } } }
2.置一個(gè)標(biāo)志,表示一個(gè)Timer處理正在執(zhí)行,下一個(gè)Timer發(fā)生的時(shí)候發(fā)現(xiàn)上一個(gè)沒有執(zhí)行完就放棄(注意這里是放棄,而不是等待哦,看看執(zhí)行結(jié)果就明白啥意思了)執(zhí)行,適用重入經(jīng)常出現(xiàn)的場景。在多線程下給inTimer賦值不夠安全,Interlocked.Exchange提供了一種輕量級的線程安全的給對象賦值的方法(感覺比較高上大,也是比較推薦的一種方法)。
private static System.Timers.Timer aTimer = new System.Timers.Timer(); private static int inTimer = 0; /// <summary> /// 設(shè)置定時(shí)器 /// </summary> public static void SetTimer() { //讀取配置時(shí)間 try { aTimer.Interval = 30000; //半分鐘觸發(fā)一次 aTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnTimedEvent); aTimer.AutoReset = true;//每到指定時(shí)間Elapsed事件是到時(shí)間就觸發(fā) aTimer.Enabled = true; //指示 Timer 是否應(yīng)引發(fā) Elapsed 事件。 } catch (Exception ex) { LogManager.RecordLog(LogType.Error, "ipad數(shù)據(jù)同步出錯(cuò):" +ex.Message,ex); } } private static void OnTimedEvent(Object source, ElapsedEventArgs e) { //如果當(dāng)前時(shí)間是為配置的準(zhǔn)點(diǎn)時(shí)間就進(jìn)來 var time =Convert.ToInt32( SynchronousHelper.AppConfig.get_Items("SycTime")); if (DateTime.Now.Hour == time && DateTime.Now.Minute == 0) { //inTimer設(shè)置一個(gè)標(biāo)志,表示一個(gè)Timer處理正在執(zhí)行,下一個(gè)Timer發(fā)生的時(shí)候發(fā)現(xiàn)上一個(gè)沒有執(zhí)行完就放棄 if (Interlocked.Exchange(ref inTimer, 1) == 0) { LogManager.RecordLog(LogType.Info, "數(shù)據(jù)開始同步時(shí)間:" + e.SignalTime, null); SetTimerStart(); System.Threading.Thread.Sleep(60000); //執(zhí)行完等待越過當(dāng)前分鐘,使整點(diǎn)內(nèi)只能進(jìn)來一次 Interlocked.Exchange(ref inTimer, 0); } } }
稍微總結(jié)一下,timer是一個(gè)使用挺簡單的類,拿來即用,這里主要總結(jié)了使用timer時(shí)重入問題的解決,以前也沒思考過這個(gè)問題,解決方案也挺簡單。這里的解決方案同時(shí)也適用多線程的重入問題。
感謝各位的閱讀!看完上述內(nèi)容,你們對C#中關(guān)于Timer定時(shí)器重入問題如何解決大概了解了嗎?希望文章內(nèi)容對大家有所幫助。如果想了解更多相關(guān)文章內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
標(biāo)題名稱:C#中關(guān)于Timer定時(shí)器重入問題如何解決
標(biāo)題URL:http://www.rwnh.cn/article18/gcggdp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供做網(wǎng)站、全網(wǎng)營銷推廣、企業(yè)建站、域名注冊、搜索引擎優(yōu)化、微信小程序
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)