setlocale 是线程安全函数吗?
我需要更改线程中的区域设置以正确解析带有 strtod() 的双精度数,我为此使用 setlocale() (C++)。它是线程安全的吗?
更新:另一个问题。当我在 main() 函数中调用 setlocale() 时,它不会影响其他更深层次的例程。为什么???代码很多,写chunk是有问题的。
I need to change locale in the thread to parse a double with strtod() correctly, I'm using setlocale() for this (C++). Is it thread safe?
Update: Another problem. When I invoke setlocale() in my main() function it doesn't affect in other routines deeper. Why??? There is a lot of code, so it's problematic to write the chunk.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
在 C++11 中,标准线程现在是该语言受支持的一部分。该标准明确指出,setlocale() 调用会与对 setlocale() 的其他调用或对受当前 C 语言环境影响的函数(包括 strtod())的调用引入数据竞争。 locale::global() 函数的行为被认为与调用 setlocale() 一样,因此它还会引入数据竞争(如下所述)。
在使用 glibc 的 Linux 上,它是 MT 不安全(const:locale env) 让线程使用非 NULL 参数同时调用 setlocale() 并调用可能使用全局语言环境的任何其他函数(数据争用,从而导致 C11 中未定义的行为)。建议使用 uselocale() 代替 MT-安全并且仅更改调用线程的区域设置。在使用 C++ 代码中的 libstdc++ 的 Linux 上,您应该避免 locale::global (进程范围内的更改)并为线程的使用创建一个区域设置(出于与 C 运行时相同的原因,locale::global 是 MT 不安全的)。鉴于您的目标是使用 strtod(一种 C API),您应该使用 uselocale()。
在使用 glibc 的 Linux 上,setlocale() 函数本身是 MT 不安全的,除非您满足 2 个严格的标准,并且按照 POSIX 的要求更改整个进程的区域设置。新的 Linux 手册页(Red Hat 和 Fujitsu 致力于指定 MT 的一部分-所有 API 的安全符号)标记 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++ 代码,您应该再次 使用 locale 类的实例并避免 locale::global。
In the C++11 standard threads are now a supported part of the language. The standard explicitly calls out that setlocale() calls introduce data races with other calls to setlocale() or calls to functions that are affected by the current C locale including strtod(). The locale::global() function is considered to behave as-if it called setlocale(), so it also can introduce a data race (noted below).
On Linux with glibc it is MT-unsafe (const:locale env) to have threads call setlocale() concurrently with a non-NULL argument and call any other function which might use the global locale (data race and thus undefined behaviour in C11). It is suggested to use uselocale() instead which is MT-safe and changes only the calling thread's locale. On Linux with libstdc++ in C++ code you should avoid locale::global (process wide change) and create a locale for the thread's use (the locale::global is MT-unsafe for the same reasons as the C runtime). Given your goal to use strtod (a C API) you should use uselocale().
On Linux using glibc the setlocale() function itself is MT-unsafe unless you meet 2 strict criteria, and as required by POSIX changes the locale for the entire process. The new Linux man pages (part of the Red Hat and Fujitsu work to specify MT-safety notations for all APIs) mark setlocale() as "MT-Unsafe const:locale env", which means that setlocale is MT-safe IFF you keep the locale constant (by not modifying it, just querying it by passing NULL), and if you keep the locale and environment constant (to avoid changes to the locale if the argument is ""). On Linux using glibc you should use uselocale() if you want to change just the locale of the calling thread, since this is MT-safe and does not rely on your environment in any way and strtod will use the thread's locale. Similarly all systems that implement POSIX should provide uselocale() for use in a thread context (MT-safe).
OS X implements uselocale() so you can use that.
On Windows use _configthreadlocale to change if setlocale() operates on the whole process or threads (turns it into uselocale which is what you need), but for C++ code you should again use an instance of the locale class and avoid locale::global.
对 setlocale() 的调用可能是也可能不是线程安全的,但区域设置本身是每个进程的,而不是每个线程的。这意味着即使您 setlocale() 是线程安全的或者您使用互斥体来保护自己,更改仍然会更新所有线程的当前区域设置。
不过,还有一个针对每个线程的替代方案:uselocale()。
该语言环境在内部使用引用计数,这就是为什么在使用 newlocale() 激活它后可以安全地释放它。
The call to setlocale() may or may not be threadsafe, but the locale setting itself is per-process, not per-thread. That means that even if you setlocale() is thread-safe or you use a mutex to protect yourself, the change will still update the current locale for all your threads.
There is a per-thread alternative though: uselocale().
The locale uses reference-counting internally, which is why it is safe for you to free it after you've activated it with newlocale().
无论您使用什么实现,您都需要查阅文档。 C++ 目前没有指定任何有关线程的内容,因此它取决于实现(您还没有告诉我们)。
例如,我的 setlocale Linux 手册页包含以下代码片段:
这并不绝对表明它是线程不安全的,但我会非常警惕。使用 NULL 调用它(即查询)很可能是线程安全的,但一旦有线程修改它,所有的赌注都会消失。
可能最安全的做法(假设它不是线程安全的)是使用互斥体保护对
setlocale
的所有调用,并有一个特殊的函数来格式化你的数字沿着以下思路:You need to consult the documentation for whatever implementation you're using. C++ doesn't currently specify anything about threads so it comes down to the implementation (which you haven't yet told us).
For example, my Linux manpage for setlocale has the snippet:
which doesn't absolutely indicate that it's thread-unsafe but I'd be very wary. It's likely that calling it with NULL (i.e., querying) would be thread-safe but as soon as you have a thread modifying it, all bets are off.
Probably the safest thing to do (assuming it isn't thread-safe) would be to protect all calls to
setlocale
with a mutex and have a special function to format your numbers along the lines of:对于 C++98,它取决于编译器、您选择的运行时库以及线程安全的确切含义。
例如,使用 MSVC 和多线程运行时,您应该在
setlocale
本身的意义上是安全的。但我不认为你会得到每个线程的区域设置。将setlocale
用于全局区域设置,而不是每个线程区域设置。C++98 不涉及线程(或者就此而言,动态库)。
For C++98 it depends on the compiler and on which runtime lib you select and on what exactly you mean by thread safe.
E.g. with MSVC and multi-threaded runtime you should be safe in the sense that
setlocale
itself is. But I don't think you'll get a per-thread locale. Usesetlocale
for a global locale, not a per-thread locale.C++98 does not address threading (or, for that matter, dynamic libraries).
C语言确实支持线程本地。请阅读http://msdn.microsoft.com/en-us/library/ms235302 .aspx。
主要方法是:_configthreadlocale(_ENABLE_PER_THREAD_LOCALE)
C language does support thread local. Please read http://msdn.microsoft.com/en-us/library/ms235302.aspx.
The main methods is: _configthreadlocale(_ENABLE_PER_THREAD_LOCALE)
解决原始问题的第二部分:
setlocale
函数位于 C 库中(由标准标头
在 C++ 环境中定义)及其使用只会影响 C 库例程。您在问题的第一部分提到了 C++,所以我想知道您是否期望 C++ 例程记录使用setlocale
所做的区域设置更改。我的经验表明他们不会。在 C++ 中处理区域设置信息的正确方法由标准 C++ 标头
中指定的库定义。该库以与 C++ I/O 操作兼容的方式提供对区域设置信息的控制。例如,您可以创建一个具有某些特征的std::locale
对象,然后将该对象注入到std::filebuf
中,以便 I/O 操作遵循这些特征。如果您在混合 C/C++ 环境中运行,请使用
std::locale::global()
——使用正确的参数类型,它还会像 C 库一样设置 C 全局区域设置使用 LC_ALL 调用了函数setlocale
。这将使 C 和 C++ 库功能保持同步。To address the second part of the original question:
The
setlocale
function is in the C library (as defined in a C++ environment by the standard header<clocale>
) and its use will only affect C library routines. You mention C++ in in the first part of your question, so I wonder if you are expecting C++ routines to take note of locale changes made withsetlocale
. My experience says they will not.The proper methods of dealing with locale information in C++ are defined by a library specified in the standard C++ header
<locale>
. This library provides control of locale information in a way which is compatible with C++ I/O operations. For example, you can create astd::locale
object with certain characteristics and then imbue astd::filebuf
with that object so that I/O operations follow those characteristics.If you are running in a mixed C/C++ environment, use
std::locale::global()
-- with the right sort of parameter, it also sets the C global locale as if the C library functionsetlocale
was called with LC_ALL. This will keep the C and C++ library functionality synchronized.