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

1. 變量初始化

這是使用 C++11 codecvt 時遇到的一個坑,轉換編碼時,mbstate_t 這個中間狀態變量,必須初始化爲0,不然運行出錯,即:promise

// 不能夠!
mbstate_t mbst;
// 這樣能夠
mbstate mbst = {0};
// 這樣也行
mbstate mbst = mbstate_t();

這是第一個坑,並不算太坑,還比較容易調試和發現,也怪本身大意了。安全

經驗:C++中的變量必定要初始化後再使用。 app

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...code

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 動態加載。對象

解決辦法也是有的:

  1. 既然不能經過 LoadLibrary 動態加載,那我靜態加載不就好了,只要在編譯 exe 時靜態連接 dll,即 dll 在 exe 的導入表中,那就能夠正常運行(這也要求 exe 必須是本身可編譯的)
  2. DllMain 中使用 TLS 相關的 API 手動初始化
  3. 祈禱 xp 早日完蛋

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

4. dll 中的靜態對象

這個坑跟上個坑是同時出現的,只是我當時用了靜態連接的方式後,就運行正常了,也就沒在乎。直到後來又想在 C# 中調用 dll,這回沒辦法靜態連接了。爲了先實現功能,我選擇了暫時刪除 thread_local,可是在 xp 上依然運行出錯,錯誤緣由跟以前同樣!臥槽,我特麼明明都刪掉了 thread_local 呀,爲什麼還這樣!!

又通過2個小時的調試,最終肯定問題出在 C++17 標準庫中的 std::experimental::filesystem::exists() 函數,可是通過我單步調試發現,這個函數並無使用 TLS,只用到了一些全局靜態對象,莫非是全局靜態對象的問題?

因而仍是找文檔吧,跟上個問題同一個網址 https://msdn.microsoft.com/en...

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 版本中。

相關文章
相關標籤/搜索