为啥 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 节适用于 <ctype.h>
。)
现在,虽然我不确定这段代码是否与 Darwin (MacOS X) 中的相同,但很可能是相似的。我认为手册页中可能有勘误的空间——目前尚不清楚需要更正的页面是atoi()
还是strtol()
。
【讨论】:
setlocale
函数不是线程安全的,如果任何其他线程可能位于其行为取决于语言环境的函数中,则可能不会调用该函数。但是,此类函数不被视为非线程安全的。关于 async-cancel-safety 的评论也似乎很奇怪,因为 POSIX 非常明确地指出除了 pthread_setcanceltype
、pthread_setcancelstate
和 pthread_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 记录为不是线程安全的?的主要内容,如果未能解决你的问题,请参考以下文章