为啥 OSX 将 atoi/atof 记录为不是线程安全的?

Posted

技术标签:

【中文标题】为啥 OSX 将 atoi/atof 记录为不是线程安全的?【英文标题】:Why does OSX document atoi/atof as not being threadsafe?为什么 OSX 将 atoi/atof 记录为不是线程安全的? 【发布时间】:2011-01-07 23:43:43 【问题描述】:

我知道 strtol 和 strtof 比 atoi/atof 更受欢迎,因为前者可以检测错误,而且当涉及到非 base-10 时,strtol 比 atoi 灵活得多。

但我仍然对某些事情感到好奇:OS X 上的“man atoi”(或 atof)(虽然不是在 Linux 上!)提到 atoi/atof 不是线程安全的。坦率地说,我很难想象 atoi 或 atof 的可能实现不是线程安全的。有人知道手册页为什么这么说吗?这些功能在 OS X 或任何其他平台上实际上是不安全的吗?如果是的话,为什么图书馆不直接用 strtol 来定义 atoi,因此是安全的?

【问题讨论】:

大多数 atoi 实现似乎只是 strtol 包装器。 我已更改此问题的标题,使其至少是一个有效问题。 developer.apple.com/library/mac/#documentation/Darwin/Reference/… 嗯,可以想象一个非线程安全的实现:atof 可以访问当前的语言环境,如果另一个线程更改了语言环境设置,这可能是不安全的。然而,正如 R.. 所指出的,现在 POSIX 似乎要求 atof 是线程安全的。 @Jukka:确实,当一个线程使用setlocale 时调用任何依赖于语言环境的函数会导致未定义的行为,但这被认为与那些线程安全的函数是分开的(根据 POSIX)或不是。基本上你不能使用setlocale,除非在创建任何线程之前或在extreme同步下。这就是在 POSIX 2008 中引入 uselocale 等的原因。 【参考方案1】:

查看 MacOS X 10.6.6 上的手册页,它记录了两个函数,atof()atof_l(),我怀疑这暗示了为什么该函数被认为不是线程安全的:

概要

#include <stdlib.h>
double atof(const char *str);

#include <xlocale.h>
double atof_l(const char *str, locale_t loc);

描述

atof() 函数将 str 指向的字符串的初始部分转换为双精度表示。

相当于:

      strtod(str, (char **)NULL);

小数点字符在程序的语言环境中定义(类别 LC_NUMERIC)。

虽然atof() 函数使用当前语言环境,但atof_l() 函数可以直接传递一个语言环境。有关详细信息,请参阅 xlocale(3)。

实施说明

atof() 函数不是线程安全的,也不是异步取消安全的。

atof() 函数已被 strtod() 弃用,不应在新代码中使用。

错误

函数atof() 发生错误时不需要影响errno 的值。

我的怀疑是,如果在执行atof() 函数时,另一个线程更改了当前语言环境,则无法保证结果。否则,似乎没有理由发出警告。


我已经四处寻找达尔文 C 库源代码的确定位置,但没有找到。如果你去atoi()的FreeBSD源代码,很明显函数实现很琐碎:

int
atoi(str)
    const char *str;

    return (int)strtol(str, (char **)NULL, 10);

(是的,甚至没有使用原型定义!)

strtol() 的手册页没有关于线程安全或异步取消安全的狡猾措辞。但是,快速查看strtol() 的源代码会发现它使用了受语言环境影响的isspace()

ISO/IEC 9899:1999,第 7.11.1.1 节 setlocale 函数

187 7.4 中唯一不受当前语言环境影响的函数是 isdigit 和 isxdigit。

(第 7.4 节适用于 &lt;ctype.h&gt;。)

现在,虽然我不确定这段代码是否与 Darwin (MacOS X) 中的相同,但很可能是相似的。我认为手册页中可能有勘误的空间——目前尚不清楚需要更正的页面是atoi() 还是strtol()

【讨论】:

setlocale 函数不是线程安全的,如果任何其他线程可能位于其行为取决于语言环境的函数中,则可能不会调用该函数。但是,此类函数不被视为非线程安全的。关于 async-cancel-safety 的评论也似乎很奇怪,因为 POSIX 非常明确地指出除了 pthread_setcanceltypepthread_setcancelstatepthread_cancel 之外没有任何函数是 async-cancel-safe。 这很有意义。只要您的应用程序不更改语言环境(我的当然不是),文档并没有更具体地说明它是安全的,这太糟糕了。我不是语言环境专家,但我不确定我是否理解如何在 atoi 中使用语言环境(atof,我理解——小数点)。 @Larry:atoi() 可能正在查看千位分隔符,也许是分组,我想...但是,请参阅我对答案的更新。【参考方案2】:

在做了一些研究之后,我认为这只是过去 errno 是一个全局变量的遗留问题。如果你检查 FreeBSD errno.h history 从 first revision 开始,你会看到它最初被定义为

extern int errno;           /* global error number */

现在它是一个函数。我真的想不出任何其他原因。

虽然atoi 一直是strtol 的包装器,它也设置了errno,然后应该具有相同的线程安全性。这一定只是文档问题。

【讨论】:

在现代 Linux 系统上,errno 是线程本地的,即每个线程都有自己的副本。【参考方案3】:

Here's the implementation of atoi() 在 Apple 的 libc 中(atof() 类似):

int
atoi(str)
    const char *str;

    return (int)strtol_l(str, (char **)NULL, 10, __current_locale());

还有strtol():

long
strtol(const char * __restrict nptr, char ** __restrict endptr, int base)

    return strtol_l(nptr, endptr, base, __current_locale());

由于 man strtol 没有提到 strtol() 的线程安全问题,您可能会得出以下几个结论中的一个或多个:

关于 atoi() 是线程不安全的文档是错误的, 他们忽略了strtol() 也是线程不安全的, 他们保守地记录了 atoi() 不承诺线程安全,即使当前的实现恰好是线程安全的, 它们已经过时了(我想这是错误的一种特殊情况)

__current_locale() 返回一个指向描述线程语言环境的结构的指针(不出所料)。但是,如果尚未设置特定于线程的语言环境,__current_locale() 将返回一个指向全局语言环境结构的指针。我认为处理全局可能是线程不安全的,但这个问题也适用于strtol()

【讨论】:

这是 FreeBSD 版本。有充分的理由相信它在 Mac OS X 上是相同的吗? 剪辑来自opensource.apple.com/source/Libc/Libc-583的存储库【参考方案4】:

这个答案是在提出问题几年后首次回答的。在我的 Mac OS X 10.8.3(大约 2013 年 3 月)上,man atoi(或man atof)读取:

IMPLEMENTATION NOTES
  The atof() and atof_l() functions are thread-safe and async-cancel-safe.

  The atof() and atof_l() functions have been deprecated by strtod() and
  strtod_l() and should not be used in new code.

所以最后一句话可能是这里从来没有线程安全问题,只有文档中的错误。

【讨论】:

【参考方案5】:

一个猜测是这些函数没有以线程安全的方式设置 errno,但这意味着 errno 在 macos 和线程上发生了一些奇怪的事情。通常 errno 是线程局部变量。

【讨论】:

您必须不遗余力地编写一个不以线程安全方式设置errno 的版本,因为简单地errno = foo; 是一种非常安全的设置方式。 在没有编译器特定扩展的情况下,在 c(或 c++)中分配除 bool 以外的任何内容不一定是线程安全的【参考方案6】:

这个问题的前提(在我编辑标题之前的原始形式)是错误的。它们是线程安全的。 POSIX 指定所有函数都是线程安全的,除非另有说明(由 POSIX 提供),并且文档没有说明这些函数不是线程安全的。 OSX 声称符合 POSIX,因此它们在 OSX 上是线程安全的,否则这是一个错误和主要的一致性问题。我假设这只是手册页中的一个错误......

【讨论】:

所以你的答案是'man atoi' 在 OSX 上是错误的?这并没有说明这个问题的前提是错误的。 是的。这不会是供应商手册页第一次出错,也不是最后一次。我仍然不知道他们为什么坚持编写自己的手册页,而不仅仅是包含 POSIX 手册页...... 哦,我是更改问题标题的人。最初的问题是“为什么 atoi/atof 不是线程安全的?” 我很高兴接受“手册页错误”,但据我所知,您只是在猜测,这让我对删除 atoi 调用的必要性(或缺乏必要性)感到紧张从我的多线程软件。有没有人有确凿的证据? @dreamlax,好吧,如果他们真的获得了认证,那么无论是谁发布它都应该被解雇。 weirdnet.nl/apple/rename.html

以上是关于为啥 OSX 将 atoi/atof 记录为不是线程安全的?的主要内容,如果未能解决你的问题,请参考以下文章

strtok/atoi/atof/atol函数用法 详解

GeometryRenderer 为啥渲染线,而不是三角形

c语言字符处理函数常见使用集合

Mac OSX 上的 XAMPP:为啥作为“守护进程”运行? [关闭]

为啥约束总是为零,故事板是不是防止边缘?

CAD制图多线命令为啥画图只显示一条线 比例 偏移这些我都设置过了 还是搞不定