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

标签: c macos


【解决方案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 historyfirst 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()

【讨论】:

【解决方案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
    猜你喜欢
    • 2012-11-20
    • 2013-10-11
    • 2020-10-10
    • 1970-01-01
    • 2016-08-14
    • 2023-03-12
    • 1970-01-01
    • 2023-03-30
    • 2012-03-22
    相关资源
    最近更新 更多