原文轉自:http://www.tanjp.com/archives/203 (即時修正和更新)
定時器實現過程遇到的問題
在服務端的程序裏,時間是一個很常用,幾乎無處不在的變量。如果不正確使用時間,將會導致災難性的數據錯誤。所以,正確使用時間非常重要!
怎樣纔算是正確使用時間?
首先,我們得知道在程序世界裏面,時間有哪些值?
1、協調世界時,又稱世界統一時間,世界標準時間,國際協調時間,簡稱UTC。世界默認以格林威治時間(GMT)爲準。
2、世界各個地區時區的時間。如北京時間GMT+8,就是格林威治時間加8小時。
3、以計算機CPU時鐘結合程序啓動的時間算出的時間戳。
4、時間輪算法每次tick算出的時間。
然後,如何算是正確使用時間?那得先想想,怎樣是不正確的?
1、有一種唯一ID生成算法依賴於時間戳,如果時間改小了,就可能導致唯一ID重複,那可就是災難性的邏輯錯誤。
2、有些依賴於系統日期時間的定時器檢測,當系統時間由於某種原因導致時間回退到以前,那將會導致定時器卡死等待。甚至,std::this_thread:sleep_for 也是有BUG的,回退時間會導致該函數掛起等待,與期待的效果相違背。
3、有些時間校驗的算法,如果就在那一瞬間,時間回退了,導致某個非法校驗通過了?那也導致錯誤。
正確使用時間,應該保證時間戳在程序運行過程總是單調遞增,這隱患兩個意思,保持遞增,不能停下,不能倒退。
最後,總結一下時間和定時器的問題。
1、把時間戳劃分爲三種:系統日期時間戳(system time),CPU時鐘時間戳(cpu time),時間輪執行後的時間戳(tw time)。
2、底層邏輯相關的必須用CPU時鐘時間戳(cpu time),如網絡心跳,超時檢測,唯一ID算法,等等。
3、業務邏輯層使用時間輪時間戳(tw time)的話,要有一套自動同步機制,保證與CPU時鐘時間戳(cpu time)單調遞增同步。
4、在進程啓動前,儘可能保證各個機器的系統時間戳已同步。
5、在測試時間相關功能時,停掉3中所說的同步機制,通過指令加快時間輪時間戳(tw time),可達到改時間測試的效果。但不能把時間回退來測試。
踩過的坑
1、std::this_thread:sleep_for 有BUG,改變系統日期時間會導致 std::this_thread:sleep_for 卡住。改用 boost::this_thread:sleep_for 替代。
2、定時器如果依賴於系統日期時間,很可能由於時間同步問題或者手動改時間導致定時器卡死。
3、唯一ID生成算法依賴於時間,所以要採用CPU時鐘作爲統計的依據,保證時間只會單調遞增。
4、boost::asio::deadline_timer 改變系統的日期時間,會導致定時器卡住,改用 boost::asio::steady_timer 替代。