慎用unsigned int減法
最近在做一個快取的清理策略時,遇到了一個詭異的bug。
快取每次命中,都會更新一個時間戳;然後定期清理的時候,用當前時間now減去快取的時間戳,如果差值大於7天,則清理掉這個快取項。
uint32_t now = time(); ... // Some other code if (now - hash_elem.timestamp > expire_interval) { erase(hash_elem); }
邏輯很簡單,剛開始單執行緒執行也沒有問題,但是在我對整個程式進行完多執行緒改造後,從log中發現,越是經常命中的快取項,就越是容易過期。這和常識是相悖的,按理說,應該越常見的快取項,則timestamp更新的越頻繁,也就越不容易過期才對。
難道是多執行緒併發寫,把timestamp寫壞了?
我又打印出了各個寫timestamp處的值,從日誌看,timestamp的數值正常。
接下來,我又把now的值打出來看,看上去也是正常的一個時間戳。
但是,仔細一看,now的值居然比hash_elem.timestamp的值要小!
原來這裡是因為在多執行緒環境下,獲取了now的值之後,hash_elem.timestamp的值又被其他執行緒更新了。
而當兩個unsigned int相減得到一個負數時,在計算機中的表示,符號位(第1位)為1。此時如果按照unsigned int來解釋,就將得到一個非常大的數字,自然大於expire_interval,也就會導致
越是頻繁命中的快取項,越容易過期
的現象。
解決這個問題兩種方式:
- 強制型別轉換,改成 int(now - hash_elem.timestamp) > expire_interval
- 使用加法替換減法,改成 now > hash_elem.timestamp + expire_interval,不過這裡要考慮加法的溢位問題。
使用unsigned int相減時,要小心。
轉載請註明出處:http://blog.guoyb.com/2018/10/27/uint-minus/
歡迎使用微信掃描下方二維碼,關注我的微信公眾號TechTalking,技術·生活·思考:
