現代 C++ 開發(1):執行緒安全註解
(題圖截自 ofollow,noindex"> https:// llvm.org/devmtg/2011-11 /Hutchins_ThreadSafety.pdf )
Muduo 最近正式釋出了 2.0.0 版 (採用 C++11),同時把 master 分支改為與 cpp11 分支同步,原來的 cpp98 分支將會繼續維護一段時間。Muduo C++11 版的主要改動見下表:

出自: 陳碩:在C++專案中,什麼時候該用Boost,什麼時候該用STL呢?
這些改動都是顯而易見的,我也不打算多費筆墨來談這些大家都知道的內容。Muduo C++11 版要求 GCC 4.7 以上或 Clang 3.5 以上,在 Debian 7、Ubuntu 14.04、CentOS 7 及其後續發行版上編譯測試通過。(Ubuntu 12.04 的 GCC 是 4.6,不支援 override 關鍵字,不能編譯 Muduo C++11 版。)
本系列我將介紹一下這個表格之外的內容——現代 C++ 開發。
在現在這個一臺 x86 伺服器可以有幾十上百個核(cores / threads)的時代,編寫正確高效的併發程式(concurrent programming)是後臺開發人員的必備技能。併發模型(Concurrency Models)有很多種(《 Seven Concurrency Models in Seven Weeks: When Threads Unravel 》),基於執行緒並用鎖來保護共享資料的模型是最原始的(primitive),恐怕也是最難用的。在編寫這種程式的時候,我們應該採用各種靜態和動態檢查工具(ThreadSanitizer 、Valgind Helgrind/DRD)來防止程式碼出現 data race。
本文介紹一個特別簡單易用的靜態檢查工具:Clang thread safety annotations,主要是這個 commit 涉及的知識點: Enable Clang Thread Safety Analysis. · chenshuo/muduo@4cc25e6 。
執行緒安全註解的基本思路是,通過程式碼註解(annotations )告訴編譯器哪些成員變數和成員函式是受哪個 mutex 保護,這樣如果忘記了加鎖,編譯器會給警告。因為在後續維護別人的程式碼時候,往往不像原作者那樣深刻理解設計意圖,特別容易遺漏執行緒安全的假設。這個工具正是為了應對這種情況,讓原作者能把設計意圖用程式碼註解清楚地寫出來並讓編譯器能自動檢查,讓後來的維護者就不容易犯低階錯誤了。
現在 Muduo C++11 版的 muduo::MutexLock 與 muduo::MutexLockGuard 都已經加上了執行緒安全註解,使用者只要在自己的程式碼中用 GUARDED_BY 表明哪個成員變數是被哪個 mutex 保護的(用法見下圖),就可以讓 clang 幫你檢查有沒有遺漏加鎖的情況了。別忘了用 clang -Wthread-safety 編譯你的程式碼。

這個工具還真的找到了一處 muduo Inspector 中忘記加鎖的地方,可見是有價值的。

其實,Clang 的文件 Thread Safety Analysis 已經講得非常清楚了,我就不搬運了。Clang 從 3.5 版起支援文件中的新版註解,而 3.4 版只支援舊版註解。這是 muduo 要求 Clang >= 3.5 的原因之一。
更深入的介紹可參考 C/C++ Thread Safety Analysis 論文: https:// research.google.com/pub s/archive/42958.pdf 和本文第一行的 slides 連結。
拿走不謝(Take-away):就算你不用 clang/llvm 來編譯產生生產環境的可執行檔案,最好也讓你的專案能用 clang 編譯通過,用於開發和測試。這樣可以利用很多基於 clang 的先進工具在開發環境下捕捉到 bug。
後文預告:使用與擴充套件 clang-tidy (原始碼檢查與修正工具)。