2010-10-30 98 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++库的功能同步。