C++ 及標準庫中的那些大坑

發表於2016-09-16

1. 變數初始化

這是使用 C++11 codecvt 時遇到的一個坑,轉換編碼時,mbstate_t 這個中間狀態變數,必須初始化為0,否則執行出錯,即:

這是第一個坑,並不算太坑,還比較容易除錯和發現,也怪自己大意了。

經驗:C++中的變數一定要初始化後再使用。

2. 匿名 std::thread 物件

這個坑要和 boost 進行比較,在 boost 中,是可以建立匿名 thread 物件的,並且這樣的匿名物件跟 future、promise是可以正常配合使用的(《Boost標準庫完全開發指南》一書中的示例程式碼就是這樣寫的)。

但是,在 C++ 標準庫中不能這麼幹,會出現莫名其妙的錯誤,除錯時也不會顯示任何有價值的資訊,最終確定這個問題真是費了我好大勁,因為根本沒想到會是這個問題,畢竟 boost 裡都正常使用了。

經驗:儘量不使用匿名物件,如果想要用完立即釋放,可以使用單獨的程式碼塊包裹。

3. 執行緒區域性儲存(TLS)

這是一個坑了我一天的大坑。

C++11 中,新引入了 thread_local 儲存型別,等同於之前的 __declspec(thread),由於其具有真正的可移植性,所以我就嘗試使用了,但這也是噩夢的開始。

我有一段程式碼,如果編譯為 exe,在 xp 系統上能正常執行,但如果編譯為 dll,在 xp 上執行就出錯。由於 xp 上不能安裝 VS 這種高科技玩意,只能用 x32_dbg 湊合除錯,發現是空指標異常,指標來源為 fs:[2c],這是 TLS 指標啊,然後百度,找到了微軟的文件 https://msdn.microsoft.com/en-us/library/y5f6w579 :

On XP systems, thread_local may not function correctly if a DLL uses thread_local data and it is loaded dynamically via LoadLibrary.

是的,如果 dll 中使用了 thread_local,這個 dll 將不能在 xp 上通過 LoadLibrary 動態載入。

解決辦法也是有的:

既然不能通過 LoadLibrary 動態載入,那我靜態載入不就行了,只要在編譯 exe 時靜態連結 dll,即 dll 在 exe 的匯入表中,那就可以正常執行(這也要求 exe 必須是自己可編譯的)
在 DllMain 中使用 TLS 相關的 API 手動初始化
祈禱 xp 早日完蛋

經驗:或許我應該拋棄 xp 了。

4. dll 中的靜態物件

這個坑跟上個坑是同時出現的,只是我當時用了靜態連結的方式後,就執行正常了,也就沒在意。直到後來又想在 C# 中呼叫 dll,這回沒辦法靜態連結了。為了先實現功能,我選擇了暫時刪除 thread_local,但是在 xp 上依然執行出錯,錯誤原因跟之前一樣!臥槽,我特麼明明都刪掉了 thread_local 呀,為何還這樣!!

又經過2個小時的除錯,最終確定問題出在 C++17 標準庫中的 std::experimental::filesystem::exists() 函式,但是經過我單步除錯發現,這個函式並沒有使用 TLS,只用到了一些全域性靜態物件,莫非是全域性靜態物件的問題?

於是還是找文件吧,跟上個問題同一個網址 https://msdn.microsoft.com/en-us/library/y5f6w579 :

Starting in C++11, a static local variable initialization is guaranteed to be thread-safe. This feature is sometimes called magic statics. However, in a multithreaded application all subsequent assignments must be synchronized. The thread-safe statics feature can be disabled by using the /Zc:threadSafeInit- flag to avoid taking a dependency on the CRT.

在 C++11 中,靜態變數的初始化是執行緒安全的,這個所謂的“執行緒安全”,就是引入了 TLS 來進行一些額外的檢查,好在這個特性是可以禁用的,編譯時新增 /Zc:threadSafeInit- 選項即可(注意最後的減號),禁用後就不會使用 TLS 了,也就可以在 xp 上動態載入了。

經驗:xp 去死吧!去死吧!去死吧!

注:這些問題在 VS2015 Update 2 中發現,應該也會持續存在於之後的 VS 版本中。

相關文章