为什么std :: localtime和std :: gmtime没有C++ 11线程安全替代品?

TNA*_*TNA 44 c++ thread-safety c++11 c++-chrono

在C++ 11中,你仍然需要使用std::localtimestd::gmtime作为间接来打印std::chrono::time_point.这些函数在C++ 11引入的多线程环境中使用是不安全的,因为它们返回一个指向内部静态结构的指针.这尤其令人讨厌,因为C++ 11引入了方便的功能std::put_time,由于同样的原因几乎无法使用.

为什么这么根本被打破或者我忽略了什么?

T.C*_*.C. 17

根据N2661,补充说<chrono>:

本文不提供日历服务,除了与C的最小映射time_t.

由于本文没有提出日期/时间库,也没有指定时期,因此它也没有解决闰秒问题.但是,日期/时间库会发现这是构建的良好基础.

本文未提出通用物理量库.

本文提出了一个坚实的基础,在未来,可以为一般的物理单元库提供兼容的起点.虽然这样一个未来的图书馆可能采取多种形式中的任何一种,但目前的提案远远没有实际成为一个物理单位图书馆.该提案是针对特定时间的,并且继续受到线程库的时间相关需求的推动.

该提议的主要目标是以易于使用,安全使用,高效且足够灵活的方式满足标准库线程API的需求,从而在10年甚至100年后不会过时.本提案中包含的每个功能都是出于特定原因而以实际用例为动机.那些属于"酷"类别的东西,或"听起来可能有用",或"非常有用但不需要此接口"的内容尚未包括在内.这些项目可能出现在其他提案中,并可能针对TR.

请注意,主要目标<chrono>是"满足标准库线程API的需求",这不需要日历服务.

  • 真正的原因是,任何与时间相关的事情都会在委员会中引起极大的争议,而且只是为了让"时间"标准化而引起轩然大波.如果我们也尝试过日历服务,C++ 11至少会被命名为C++ 14.http://home.roadrunner.com/~hinnant/bloomington/date.html是委员会讨厌的后C++ 11日历提案. (9认同)
  • 在我看来,如果用C++函数替换C函数,它应该在某种程度上是一致的,你不需要不安全的C函数来使用它.除了某些特殊情况之外,没有保存,独立于平台的方式来获取`tm`所以使用`tm`的所有​​函数都无法使用C++ 11.它没有标准的方法. (2认同)
  • @CodesInChaos:因为 `localtime`/`gmtime` API 比位还旧。;-) (2认同)
  • 我上面提到的"委员会讨厌的日历提案"的链接已经消失.这是旧链接的新版本:http://howardhinnant.github.io/date.html此外,这是一个大大改进的"版本2"的库:http://howardhinnant.github.io/date_v2.html这是基于github的实现,目前由3个不同的(但相关的库)组成:https://github.com/HowardHinnant/date (2认同)

Mat*_*son 11

localtime并且gmtime具有静态的内部存储,这意味着它们不是线程安全的(我们必须返回指向数据结构的指针,因此它必须动态分配,静态值或全局值 - 因为动态分配会泄漏内存,这不是一个合理的解决方案,这意味着它必须是一个全局变量或静态变量[理论上,可以在TLS中分配和存储,并使其成为线程安全的方式]).

大多数系统都有线程安全的替代品,但它们不是标准库的一部分.例如,Linux/Posix有localtime_rgmtime_r,它为结果提供了额外的参数.例如,参见 http://pubs.opengroup.org/onlinepubs/7908799/xsh/gmtime.html

类似地,Microsoft库也具有gmtime_s重入性,并以类似的方式工作(将输出参数作为输入传递).请参阅http://msdn.microsoft.com/en-us/library/3stkd9be.aspx

至于为什么标准C++ 11库不使用这些功能?你必须问那些编写该规范的人 - 我希望它具有可移植性和便利性,但我并不完全确定.

  • 请注意,Microsoft的Visual C运行时中的`localtime`和`gmtime`确实使用TLS并且是线程安全的.(参见[here](http://stackoverflow.com/q/2278919/25507).)`gmtime_s`的目的是安全性(添加参数验证),而不是重入(尽管它需要一个输出参数才能让它也适用于此). (5认同)

Yak*_*ont 6

没有线程安全替代方案std::localtime,std::gmtime因为您没有提出并在整个标准化过程中编组它.其他人也没有.

chrono唯一的日历代码是包装现有time_t功能的代码.标准化或编写新标准不属于chrono项目范围.执行此类标准化需要更多时间,更多工作,并添加更多依赖项.简单地包装每个time_t函数很简单,依赖性很少,而且很快.

他们狭隘地集中精力.他们成功地关注了他们所关注的事情.

我鼓励您开始工作<calendar>或加入此类工作,以便为其创建强大的日历API std.祝你好运!


How*_*ant 5

如果您愿意使用免费的、开源的 3rd 方库,这里有一种以std::chrono::system_clock::time_pointUTC打印的方法:

#include "date.h"
#include <iostream>

int
main()
{
    using namespace date;
    using namespace std::chrono;
    std::cout << system_clock::now() << " UTC\n";
}
Run Code Online (Sandbox Code Playgroud)

这是std::gmtime使用现代 C++ 语法的线程安全替代方案。

对于现代的、线程安全的std::localtime替换,您需要这个密切相关的更高级别的时区库,其语法如下所示:

#include "tz.h"
#include <iostream>

int
main()
{
    using namespace date;
    using namespace std::chrono;
    std::cout << make_zoned(current_zone(), system_clock::now()) << "\n";
}
Run Code Online (Sandbox Code Playgroud)

这两者都将以您system_clock支持的任何精度输出,例如:

2016-07-05 10:03:01.608080 EDT
Run Code Online (Sandbox Code Playgroud)

(macOS 上的微秒)

这些库远远超出了一个gmtimelocaltime替代。例如,您想查看儒略历中的当前日期吗?

#include "julian.h"
#include <iostream>

int
main()
{
    using namespace std::chrono;
    std::cout << julian::year_month_day(date::floor<date::days>(system_clock::now())) << "\n";
}

2016-06-22
Run Code Online (Sandbox Code Playgroud)

现在的 GPS 时间怎么样?

#include "tz.h"
#include <iostream>

int
main()
{
    using namespace date;
    std::cout << std::chrono::system_clock::now() << " UTC\n";
    std::cout << gps_clock::now() << " GPS\n";
}

2016-07-05 14:13:02.138091 UTC
2016-07-05 14:13:19.138524 GPS
Run Code Online (Sandbox Code Playgroud)

https://github.com/HowardHinnant/date

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0355r0.html

更新

“date.h”和“tz.h”库现在在 C++2a 规范草案中,有非常小的变化,我们希望 'a' 是 '0'。它们将位于标题<chrono>和下方namespace std::chrono(并且不会有date namespace)。