【发布时间】:2023-03-30 03:16:01
【问题描述】:
我需要更改线程中的语言环境以正确解析带有 strtod() 的双精度,我为此使用 setlocale() (C++)。它是线程安全的吗?
更新:另一个问题。当我在 main() 函数中调用 setlocale() 时,它不会更深入地影响其他例程。为什么???代码很多,所以写chunk是有问题的。
【问题讨论】:
标签: c++ parsing double setlocale
我需要更改线程中的语言环境以正确解析带有 strtod() 的双精度,我为此使用 setlocale() (C++)。它是线程安全的吗?
更新:另一个问题。当我在 main() 函数中调用 setlocale() 时,它不会更深入地影响其他例程。为什么???代码很多,所以写chunk是有问题的。
【问题讨论】:
标签: c++ parsing double setlocale
在 C++11 中,标准线程现在是该语言支持的一部分。该标准明确指出,setlocale() 调用与对 setlocale() 的其他调用或对受当前 C 语言环境影响的函数(包括 strtod() )的调用引入了数据竞争。 locale::global() 函数的行为就像调用了 setlocale() 一样,因此它也可以引入数据竞争(如下所述)。
在带有 glibc 的 Linux 上,MT-unsafe (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 and Fujitsu work to specify MT-safety notations for all APIs 的一部分)将setlocale() 标记为“MT-Unsafe const:locale env”,这意味着 setlocale 是 MT 安全的它通过传递 NULL),并且如果您保持语言环境和环境不变(如果参数是“”,则避免更改语言环境)。在使用 glibc 的 Linux 上,如果您只想更改调用线程的语言环境,则应该使用 uselocale(),因为这是 MT 安全的,并且不会以任何方式依赖您的环境,并且 strtod 将使用线程的语言环境。同样,所有实现 POSIX 的系统都应该提供 uselocale() 以在线程上下文中使用(MT 安全)。
OS X 实现了 uselocale(),所以你可以使用它。
在 Windows 上,如果 setlocale() 在整个进程或线程上运行,则使用 _configthreadlocale 进行更改(将其转换为您需要的 uselocale),但对于 C++ 代码,您应该再次使用 use an instance of the locale class and avoid locale::global。
【讨论】:
If you use _configurethreadlocale to enable a per-thread locale, we recommend that you call setlocale or _wsetlocale to set the preferred locale in that thread immediately afterward. 因为我会为此在线程中调用 setlocale,所以我还需要在那里调用 _configurethreadlocale
对 setlocale() 的调用可能是线程安全的,也可能不是线程安全的,但语言环境设置本身是每个进程的,而不是每个线程的。这意味着即使您 setlocale() 是线程安全的,或者您使用互斥锁来保护自己,更改仍会更新所有线程的当前语言环境。
虽然有一个每个线程的替代方案:uselocale()。
#include <xlocale.h>
locale_t loc = newlocale(LC_ALL_MASK, "nl_NL", NULL);
uselocale(loc);
freelocale(loc)
// Do your thing
语言环境在内部使用引用计数,这就是为什么在使用 newlocale() 激活它之后释放它是安全的。
【讨论】:
您需要查阅文档以了解您正在使用的任何实现。 C++ 目前没有指定任何关于线程的内容,所以它归结为实现(你还没有告诉我们)。
例如,我的 Linux 手册页的 setlocale 有 sn-p:
这个字符串可以分配在静态存储中。
这并不绝对表明它是线程不安全的,但我会非常小心。 很可能使用 NULL 调用它(即查询)将是线程安全的,但是一旦你有一个线程对其进行修改,所有的赌注都没有了。
可能最安全要做的事情(假设它不是线程安全的)是使用互斥锁保护对setlocale的所有调用,并具有一个特殊功能来格式化您的号码行:
claim mutex
curr = setlocale to specific value
format number to string
setlocale to curr
release mutex
return string
【讨论】:
对于 C++98,这取决于编译器、您选择的运行时库以及线程安全的确切含义。
例如使用 MSVC 和多线程运行时,就setlocale 本身而言,您应该是安全的。但是我认为您不会获得每个线程的语言环境。将setlocale 用于全局语言环境,而不是每个线程的语言环境。
C++98 不涉及线程(或者,就此而言,动态库)。
【讨论】:
C 语言确实支持线程本地。请阅读http://msdn.microsoft.com/en-us/library/ms235302.aspx。 主要方法是:_configthreadlocale(_ENABLE_PER_THREAD_LOCALE)
【讨论】:
解决原始问题的第二部分:
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++ 库功能保持同步。
【讨论】: