我需要更改线程中的语言环境以正确解析带有strtod()的double,我正在使用setlocale()(C ++)。它是线程安全的吗?
更新:另一个问题。当我在main()函数中调用setlocale()时,它不会更深入地影响其他例程。为什么???有很多代码,所以编写块有问题。
答案 0 :(得分:7)
对setlocale()的调用可能是线程安全,也可能不是线程安全,但是语言环境设置本身是按进程而不是每个线程。这意味着,即使setlocale()是线程安全的,或者您使用互斥锁来保护自己,更改仍将更新所有线程的当前区域设置。
但是有一个每线程的替代方案:uselocale()。
#include <xlocale.h>
locale_t loc = newlocale(LC_ALL_MASK, "nl_NL", NULL);
uselocale(loc);
freelocale(loc)
// Do your thing
语言环境在内部使用引用计数,这就是为什么在用newlocale()激活它之后释放它是安全的。
答案 1 :(得分:7)
在C ++ 11标准中,线程现在是该语言的受支持部分。该标准明确指出setlocale()调用引入了对setlocale()的其他调用的数据争用,或者调用受当前C语言环境影响的函数,包括strtod()。 locale :: global()函数被认为是行为 - 如果它调用setlocale(),所以它也可以引入数据竞争(如下所示)。
在使用glibc的Linux上,MT-unsafe (const:locale env)让线程与非NULL参数同时调用setlocale()并调用可能使用全局语言环境的任何其他函数(数据竞争,因此C11中的未定义行为)。建议使用uselocale()代替MT-safe并仅更改调用线程的语言环境。在使用C ++代码中的libstdc ++的Linux上,您应该避免使用locale :: global(进程范围更改)并为线程的使用创建一个语言环境(locale :: global出于MT安全性的原因与C运行时相同)。鉴于您的目标是使用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-safe)。
OS X实现了uselocale(),因此你可以使用它。
在Windows上使用_configthreadlocale更改setlocale()是否对整个进程或线程进行操作(将其转换为您需要的uselocale),但对于C ++代码,您应该再次use an instance of the locale class and avoid locale::global。
答案 2 :(得分:3)
您需要查阅文档以了解您正在使用的任何实现。 C ++目前没有指定任何关于线程的内容,所以它归结为实现(你还没有告诉我们)。
例如,我的setlocale的Linux联机帮助页有以下代码段:
此字符串可以在静态存储中分配。
这并不是绝对表明它的线程不安全但我会非常谨慎。使用NULL(即查询)调用它的可能是将是线程安全的,但是一旦你有一个线程修改它,所有的赌注都会关闭。
可能最安全要做的事情(假设它不是线程安全的)将保护所有使用互斥锁的setlocale
调用,并具有格式化数字的特殊功能沿着这条线:
claim mutex
curr = setlocale to specific value
format number to string
setlocale to curr
release mutex
return string
答案 3 :(得分:2)
对于C ++ 98,它取决于编译器以及您选择的运行时库以及线程安全的确切含义。
E.g。使用MSVC和多线程运行时,从setlocale
本身来看,您应该是安全的。但我认为你不会得到一个每线程的语言环境。将setlocale
用于全局区域设置,而不是每线程区域设置。
C ++ 98不涉及线程(或者,就此而言,动态库)。
答案 4 :(得分:1)
C语言支持本地线程。请阅读http://msdn.microsoft.com/en-us/library/ms235302.aspx。 主要方法是:_configthreadlocale(_ENABLE_PER_THREAD_LOCALE)
答案 5 :(得分:0)
解决原问题的第二部分:
setlocale
函数位于C库中(由标准头<clocale>
在C ++环境中定义),其使用仅影响C库例程。你在问题的第一部分提到了C ++,所以我想知道你是否期望C ++例程注意到setlocale
所做的语言环境更改。我的经验表明他们不会。
在C ++中处理语言环境信息的正确方法是由标准C ++标题<locale>
中指定的库定义的。该库以与C ++ I / O操作兼容的方式提供对区域设置信息的控制。例如,您可以创建具有某些特征的std::locale
对象,然后为该对象填充std::filebuf
,以便I / O操作遵循这些特征。
如果您在混合C / C ++环境中运行,使用std::locale::global()
- 使用正确的参数类型,它还会设置C全局语言环境,就像调用C库函数setlocale
一样与LC_ALL。这将使C和C ++库功能保持同步。