【问题标题】:Is setlocale thread-safe function?setlocale 是线程安全的函数吗?
【发布时间】:2023-03-30 03:16:01
【问题描述】:

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

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

【问题讨论】:

    标签: c++ parsing double setlocale


    【解决方案1】:

    在 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

    【讨论】:

    • 在 Windows 上,您应该使用特殊的 I18N 格式函数,例如 GetDateFormatEx 而不是 strftime 等。在 macOS 上,即使不使用可可 GUI 也可以使用 NSFormater。说真的,在 Unix 上提供的 1980 年代解决方案很糟糕。
    • 写得好。你有参考吗?
    • 添加了对上游手册页工作的引用以及带有 MT 安全说明的 glibc 手册页。
    • 添加了对较新 C++11 行为的引用,现在包括线程和有关 setlocale() 的注释。
    • 有趣的旁注:我找不到任何文档是否 _configthreadlocale 实际上是线程安全的。来自文档: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
    【解决方案2】:

    对 setlocale() 的调用可能是线程安全的,也可能不是线程安全的,但语言环境设置本身是每个进程的,而不是每个线程的。这意味着即使您 setlocale() 是线程安全的,或者您使用互斥锁来保护自己,更改仍会更新所有线程的当前语言环境。

    虽然有一个每个线程的替代方案:uselocale()。

    #include <xlocale.h>
    
    locale_t loc = newlocale(LC_ALL_MASK, "nl_NL", NULL);
    uselocale(loc);
    freelocale(loc)
    // Do your thing
    

    语言环境在内部使用引用计数,这就是为什么在使用 newlocale() 激活它之后释放它是安全的。

    【讨论】:

    【解决方案3】:

    您需要查阅文档以了解您正在使用的任何实现。 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
    

    【讨论】:

    • 较新的 linux 手册页项目更新绝对包含 MT 安全信息。向下滚动页面到“ATTRIBUTES”以查看定义函数安全性的“MT-unsafe const:locale env”。这个标记是 linux 手册页项目和 glibc 之间长达一年的合作的一部分,旨在开发这种标记供开发人员使用。然后“man 7 attributes”看看如何解决 const:locale 和 env 标记以尝试使其安全。
    【解决方案4】:

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

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

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

        解决原始问题的第二部分:

        setlocale 函数位于 C 库中(在 C++ 环境中由标准头文件 &lt;clocale&gt; 定义),它的使用只会影响 C 库例程。您在问题的第一部分中提到了 C++,所以我想知道您是否希望 C++ 例程注意到使用 setlocale 所做的语言环境更改。我的经验表明他们不会。

        在 C++ 中处理语言环境信息的正确方法由标准 C++ 标头 &lt;locale&gt; 中指定的库定义。该库以与 C++ I/O 操作兼容的方式提供对语言环境信息的控制。例如,您可以创建一个具有某些特征的 std::locale 对象,然后用该对象填充 std::filebuf,以便 I/O 操作遵循这些特征。

        如果您在混合 C/C++ 环境中运行,请使用 std::locale::global() -- 使用正确类型的参数,它还会设置 C 全局语言环境,就像使用 LC_ALL 调用 C 库函数 setlocale 一样。这将使 C 和 C++ 库功能保持同步。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-05-24
          • 2021-12-16
          相关资源
          最近更新 更多