C# System.Timers.Timer中的坑,程式異常退出後timer依然執行問題
問題背景
C#小白,由於本公司IM系統服務端(java)是本人獨立開發的,加上現在所在專案需要對接IM系統,於是IM的客戶端(C#實現)對接工作就交給我了。於是C#小白的我天真的以為只要呼叫C#端的SDK介面真搞定了。起初都還好,對接工作都很正常,沒什麼大問題。可是隨著時間的不斷流逝,終於在專案組小夥伴的不斷使用中發現經常登不上IM系統,然而讓我過去除錯的時候又發現是正常的,讓人很抓狂有木有!
直到他們再次重現BUG給我看的時候,我發現服務端日誌中不斷收到登入請求,而且他們程式關閉之後還是這樣,小夥伴們百思不得其解,都懷疑我的服務端程式碼有問題。對服務端程式碼很熟悉的我自然不會認為服務端會有什麼問題,因為登入只有收到客戶端的UDP請求才會觸發,而客戶端不斷髮送登入請求這種情況也只有客戶端自動重連機制了。而客戶端正常退出時 根本不會觸發自動重連機制,因為正常退出時會發出登出請求,並且會釋放自動重連,心跳以及QoS等資源,那就只有異常退出時可能會出現這種問題了,如:工作管理員強制殺死程式、程式卡死、閃退等情況,他們在開發除錯的時候也經常通過IDE直接關閉。於是我做了一個實驗,在客戶端登入成功後,反覆n次正常登陸登出,沒有任何問題;然後又嘗試了異常退出,雖然沒有立即登出IM系統(沒發登出請求嘛),但也不會出現瘋狂重連的情況,當然10s沒有心跳伺服器自然認為使用者掉線了,一切正常;下面開始重頭戲了,我改了客戶端SDK的程式碼,在登陸成功後直接呼叫自動重連機制,然後工作管理員強制殺死程式,果不其然,BUG重現了。。。
問題探究
問題已經很明確了,非正常情況下的程式關閉並且自動重連程式在執行中的時候服務端會不斷收到登入請求。到底是什麼原因導致的呢?於是我不(忐)慌(忑)不忙(安)的去看了下客戶端SDK中自動重連部分的程式碼,這裡主要依靠timer定時器每隔2s發一次登入請求,直到登入成功為止。琢磨了一會兒也沒發現有什麼問題,慢慢的我開始猜測會不會是這timer有問題?於是把timer換成了Thread,通過Thread.sleep(2000)加是否重連成功的狀態位來達到定時的效果,然後心懷期待的去測試,依然是在登陸成功後直接呼叫自動重連機制,然後工作管理員強制殺死程式,BUG消失了。一時興奮的我多測試了幾次,終於可以確定是timer的問題了,欣喜若狂的我心裡不免開始了咒罵,哪有程序都殺死了,執行緒還在跑的道理嘛!!!
解決問題
C#中的timer定時器分為三種:基於伺服器的計時器System.Timers.Timer、執行緒計時器System.Threading.Timer以及基於 Windows 的標準計時器System.Windows.Forms.Timer。其中第一種和第二種都是通過threadpool新開一個執行緒,因此效率較高;而第三種則是通過windows訊息機制實現,和它所屬的Form屬於同一個執行緒,故效率較低。由於IM是不依賴於窗體的,屬於後臺獨立執行緒,故排除第三種的使用可能。而IMSDK中使用的是第一種System.Timers.Timer而出現的問題,故我選擇使用第二種執行緒計時器System.Threading.Timer來代替,經過大量測試後,完美解決問題,故記錄一番。
至於System.Timers.Timer為什麼會出現這種情況,在下學疏才淺,實在不知道為什麼,希望知道的大神能點撥一二,不勝感激!