9

我需要更改线程中的语言环境以正确解析带有 strtod() 的 double,我为此使用 setlocale() (C++)。它是线程安全的吗?

更新:另一个问题。当我在 main() 函数中调用 setlocale() 时,它不会更深入地影响其他例程。为什么???代码很多,所以写chunk是有问题的。

4

6 回答 6

8

在 C++11 中,标准线程现在是该语言支持的部分。该标准明确指出,setlocale() 调用与对 setlocale() 的其他调用或对受当前 C 语言环境影响的函数(包括 strtod() )的调用引入了数据竞争。locale::global() 函数的行为就像它调用了 setlocale() 一样,因此它也可以引入数据竞争(如下所述)。

在带有 glibc 的 Linux 上,让线程使用非 NULL 参数同时调用 setlocale() 并调用可能使用全局语言环境的任何其他函数是MT-unsafe (const:locale env) (数据竞争,因此 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 的一部分工作为所有 API 指定 MT 安全符号)标记setlocale()作为“MT-Unsafe const:locale env”,这意味着 setlocale 是 MT-safe 如果您保持语言环境不变(通过不修改它,只是通过传递 NULL 来查询它),并且如果您保持语言环境和环境不变(到如果参数是“”,请避免更改语言环境)。在使用 glibc 的 Linux 上,如果您只想更改调用线程的语言环境,则应该使用 uselocale(),因为这是 MT 安全的,并且不会以任何方式依赖您的环境,并且 strtod 将使用线程的语言环境。同样,所有实现 POSIX 的系统都应该提供 uselocale() 以在线程上下文中使用(MT 安全)。

OS X 实现了 uselocale() 所以你可以使用它。

在 Windows 上,如果 setlocale() 在整个进程或线程上运行,则使用 _configthreadlocale 进行更改(将其转换为您需要的 uselocale),但对于 C++ 代码,您应该再次使用 locale 类的实例并避免使用 locale::global

于 2016-02-23T16:42:35.103 回答
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() 激活它之后释放它是安全的。

于 2011-06-10T08:55:58.477 回答
3

您需要查阅文档以了解您正在使用的任何实现。C++ 目前没有指定任何关于线程的内容,所以它归结为实现(你还没有告诉我们)。

例如,我的 setlocale 的 Linux 手册页有以下代码段:

该字符串可以分配在静态存储中。

这并不绝对表明它是线程不安全的,但我会非常小心。使用 NULL 调用它(即查询)很可能是线程安全的,但是一旦你有一个线程对其进行修改,所有的赌注都会被取消。

可能最安全的做法(假设它不是线程安全的)是setlocale使用互斥锁保护所有调用,并具有特殊功能来格式化您的数字,如下所示:

claim mutex
curr = setlocale to specific value
format number to string
setlocale to curr
release mutex
return string
于 2010-10-30T05:05:51.460 回答
2

对于 C++98,它取决于编译器、您选择的运行时库以及线程安全的确切含义。

例如,使用 MSVC 和多线程运行时,就其setlocale本身而言,您应该是安全的。但是我认为您不会获得每个线程的语言环境。用于setlocale全局语言环境,而不是每个线程的语言环境。

C++98 不涉及线程(或者,就此而言,动态库)。

于 2010-10-30T05:07:06.360 回答
1

C 语言确实支持线程本地。请阅读http://msdn.microsoft.com/en-us/library/ms235302.aspx。主要方法是:_configthreadlocale(_ENABLE_PER_THREAD_LOCALE)

于 2013-12-31T17:43:30.083 回答
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 全局语言环境,就像setlocale使用 LC_ALL 调用 C 库函数一样。这将使 C 和 C++ 库功能保持同步。

于 2017-03-20T13:13:55.510 回答