。

2直覺常常是錯的,要靠數(shù)據(jù)說話。

比如線程。切換到底有多大開銷,普通 mutex 加鎖到底有多大代價,系統(tǒng)調(diào)用的開銷如何,gettimeofday() 在 x86-64 Linux 是不是真的系統(tǒng)調(diào)用等等,都要靠數(shù)據(jù)說話。

3.  知道什么時候把一個鎖拆成多個,并知道什么時候不必這樣做。

除了把全局鎖拆成多個鎖,另外一種常用的避免線程爭用 (contention) 的辦法是減少加鎖的范圍。比方說從共享的數(shù)據(jù)結(jié)構(gòu)里移除 (remove and delete) 元素,其實 delete 這一步可以放到鎖外面。

4. 警惕讀寫鎖。

初學者常犯的一個錯誤是,見到某個數(shù)據(jù)結(jié)構(gòu)頻繁讀而很少寫,那么就把 mutex 替換為 rwlock。這不見得是正確的。

這條深得我心,muduo thread lib 目前就沒有提供讀寫鎖的封裝。另外,這一條也能鑒別另一篇關(guān)于線程爭用的文章不靠譜。

5. 考慮用每個 CPU 用一個鎖。

6. 知道什么時候用單個喚醒,什么時候用廣播喚醒。

notifyAll() 通常表示狀態(tài)變更,而 notify() 通常表示資源變得可用。濫用 notifyAll() 會導(dǎo)致驚群現(xiàn)象。

 muduo thread lib 的 ThreadPool 區(qū)分使用 notify() 和 notifyAll(),可作參考。

7. 學會驗尸。

 在程序中只使用 Scoped locking 來加鎖的話,很容易從 call stack 查出死鎖。參考《多線程服務(wù)器的常用編程模型》第 6 節(jié) 線程間同步。

8. 設(shè)計系統(tǒng),使之能擴充。

 比方說,把對對象的修改操作都挪到同一個線程,這樣就不必加鎖。參考 muduo 的 EventLoop::runInLoop()。

9. 如果 Mutex 就能解決問題的話,不要使用信號量 semaphore。

muduo thread lib 有意識地不提供信號量的封裝。

10.考慮用內(nèi)存“退休”法來實現(xiàn)哈希表的按桶加鎖。

11. 知道什么是偽共享。

跟多 CPU 的 Cache 有關(guān),值得了解。

12.  考慮使用非阻塞的加鎖來觀察線程爭用。

13. 在重新加鎖時,考慮使用版本號來檢測狀態(tài)變更。

14. 只在別無它法時才使用無鎖數(shù)據(jù)結(jié)構(gòu)。

15. 準備接受成功的喜悅和失敗的痛苦。

更詳細的解釋請看原文。

Bryan Cantrill 是 dtrace 的主要作者,Jeff Bonwick 是 ZFS 和 Slab allocator 的發(fā)明者。

分享到

hanrui

相關(guān)推薦