2010-10-30 142 views
9

我需要更改線程中的語言環境以正確解析strtod()的雙精度型,我使用setlocale()爲此(C++)。線程安全嗎?是否是setlocale線程安全函數?

更新:另一個問題。當我在main()函數中調用setlocale()時,它不會影響其他例程的更深層次。爲什麼???有很多代碼,所以編寫塊是有問題的。

回答

3

您需要查閱文檔,瞭解您正在使用的任何實現。 C++目前沒有指定關於線程的任何內容,因此它涉及到實現(您尚未告訴我們)。

例如,我的Linux聯機幫助頁的setlocale有片段:

此字符串可以在靜態存儲分配。

這並不表示它是線程不安全的,但我會非常謹慎。它是可能,它調用NULL(即查詢)將是線程安全的,但只要你有一個線程修改它,所有的投注都關閉。

也許是最安全的的事情(假設它不是線程安全的)將是保護所有呼叫setlocale用互斥鎖和有特殊功能的線沿線的格式化你的號碼:

claim mutex 
curr = setlocale to specific value 
format number to string 
setlocale to curr 
release mutex 
return string 
+0

有一個,謝謝 – milo 2010-10-30 05:49:31

+0

較新的Linux手冊頁項目更新絕對包含MT安全信息。向下滾動頁面到「ATTRIBUTES」看到「MT-unsafe const:locale env」,它定義了函數的安全性。這個標記是Linux man-pages項目和glibc之間爲開發人員使用開發這種標記而進行爲期一年的合作的一部分。然後,'man 7 attributes'查看如何解決const:locale和env標記以儘可能保證安全。 – 2016-12-14 15:52:15

2

對於C++ 98,它取決於編譯器以及您選擇哪個運行時庫以及線程安全的意義。

E.g.使用MSVC和多線程運行時,您應該在setlocale本身就是安全的。但我不認爲你會得到每線程的語言環境。將setlocale用於全局區域設置,而不是每個線程區域設置。

C++ 98沒有解決線程化(或者就此而言,動態庫)。

7

對setlocale()的調用可能是線程安全的,也可能不是線程安全的,但本地化設置本身是按進程而不是按線程。這意味着,即使setlocale()是線程安全的,或者您使用互斥鎖來保護自己,該更改仍會更新當前所有線程的語言環境。

雖然有一個per-thread選項:uselocale()。

#include <xlocale.h> 

locale_t loc = newlocale(LC_ALL_MASK, "nl_NL", NULL); 
uselocale(loc); 
freelocale(loc) 
// Do your thing 

區域設置使用引用計數的內部,這就是爲什麼它是安全的,你釋放它,你已經與newlocale激活後()。

+0

更確切地說,語言環境設置*可以*每個進程,請參閱https://msdn.microsoft.com/en-us/library/26c0tb7x.aspx – Lev 2015-04-12 08:28:09

5

在C++ 11中,標準線程現在是該語言的支持部分。該標準明確地聲明setlocale()調用通過對setlocale()的其他調用引入數據競爭,或者調用受當前C語言環境(包括strtod())影響的函數。如果locale :: global()函數調用setlocale(),則它被認爲是行爲,所以它也可以引入一個數據競爭(如下所述)。

在帶有glibc的Linux上,它是MT-unsafe (const:locale env)讓線程調用setlocale()並行使用非NULL參數並調用可能使用全局語言環境(數據競爭和C11中的未定義行爲)的任何其他函數。建議使用uselocale()而不是MT-safe,並且僅更改調用線程的語言環境。在使用C++代碼的libstdC++的Linux上,您應該避免使用locale :: global(進程範圍更改)併爲線程的使用創建語言環境(locale :: global與C運行時相同的原因是MT不安全)。鑑於你的目標是使用strtod(一個C API),你應該使用uselocale()。

在使用glibc的Linux上,setlocale()函數本身是MT不安全的,除非您滿足2個嚴格條件,並且POSIX要求更改整個進程的語言環境。新的Linux手冊頁(Red Hat and Fujitsu work to specify MT-safety notations for all APIs的一部分)將setlocale()標記爲「MT-Unsafe const:locale env」,這意味着setlocale是MT安全的IFF,您保持語言環境不變(通過不修改它,僅通過傳遞NULL來查詢它),並且保持語言環境和環境不變(以避免在參數爲「」的情況下更改語言環境)。在使用glibc的Linux上,如果您只想更改調用線程的語言環境,則應該使用uselocale(),因爲這是MT安全的,不會以任何方式依賴於您的環境,並且strtod將使用該線程的語言環境。同樣,所有實現POSIX的系統都應該提供uselocale()以用於線程上下文(MT安全)。

OS X實現了uselocale(),因此您可以使用它。

在Windows上,使用_configthreadlocale來更改setlocale()是否在整個進程或線程上運行(將其轉換爲您所需的uselocale),但對於C++代碼,您應該再次使用use an instance of the locale class and avoid locale::global

+0

在Windows上,您應該使用特殊的I18N格式函數,如GetDateFormatEx而不是strftime等。在macOS上,即使不使用可可GUI時也使用NSFormater。嚴重的是在Unix上提供的1980年代解決方案糟透了。 – Lothar 2016-07-01 15:36:15

+0

寫得很好。你有任何參考? – 2016-12-13 09:19:36

+0

通過MT安全說明爲上游手冊頁和glibc手冊頁添加了參考。 – 2016-12-14 16:02:16

0

爲了解決原來的問題的第二部分:

setlocale功能是在C庫和它的使用將僅影響C庫例程(如通過標準頭<clocale>在C++環境中定義)。你在問題的第一部分提到C++,所以我想知道你是否期待C++例程注意用setlocale做出的區域設置更改。我的經驗表明他們不會。

處理C++語言環境信息的正確方法是由標準C++頭文件<locale>中指定的庫定義的。該庫以與C++ I/O操作兼容的方式提供對區域設置信息的控制。例如,您可以創建具有某些特徵的std::locale對象,然後將該對象插入std::filebuf,以便I/O操作遵循這些特徵。

如果您在混合C/C++環境中運行,請使用std::locale::global() - 使用正確的排序參數,它還會設置C全局區域設置,就好像使用LC_ALL調用C庫函數setlocale一樣。這將保持C和C++庫的功能同步。