为什么没有C++11线程安全替代std::localtime和std::gmtime
Why is there no C++11 threadsafe alternative to std::localtime and std::gmtime?
在C++11中,您仍然必须使用std::localtime
和std::gmtime
作为间接打印std::chrono::time_point
。这些函数在C++11中引入的多线程环境中使用是不安全的,因为它们返回一个指向内部静态结构的指针。这尤其令人讨厌,因为C++11引入了方便的函数std::put_time
,由于同样的原因,该函数几乎不可用。
为什么这一点如此根本性地被打破,或者我忽略了什么?
根据N2661,添加<chrono>
:的论文
本文不提供日历服务,除了映射到C的CCD_ 6。
由于本文没有提出日期/时间库,也没有指定时期,因此也没有涉及闰秒。但是,日期/时间图书馆会发现这是一个很好的基础建筑
本文没有提出一个通用的物理量图书馆
本文提出了一个坚实的基础,在未来,可以为通用物理单元提供一个兼容的起点图书馆虽然这种未来的图书馆可能采取几种形式中的任何一种,目前的提议远远没有达到实际的效果单位图书馆。这项提议是有时间限制的,并将继续受线程库的时间相关需求的激励。
该提案的主要目标是满足以易于使用、安全的方式对API进行标准库线程处理使用,高效,灵活,不会过时10甚至100年后。此提案中包含的每个功能都在此处出于特定的原因,以实际用例为动机。事情这属于"酷"的范畴,或者"听起来可能是有用的",或"非常有用但此接口不需要"包括在内。此类项目可能出现在其他提案中,以及可能针对TR
请注意,<chrono>
的主要目标是"满足标准库线程API的需求",这不需要日历服务。
CCD_ 8和CCD_,这意味着它们不是线程安全的(我们必须返回一个指向数据结构的指针,所以它要么是动态分配的,要么是静态值,要么是全局值——因为动态分配会泄漏内存,这不是一个合理的解决方案,这意味着它必须是全局或静态变量[理论上,可以在TLS中分配和存储,并以这种方式使其线程安全])。
大多数系统都有线程安全的替代方案,但它们不是标准库的一部分。例如,Linux/Posix有localtime_r
和gmtime_r
,这两个参数会为结果增加一个参数。请参见示例http://pubs.opengroup.org/onlinepubs/7908799/xsh/gmtime.html
类似地,Microsoft库有gmtime_s
,它也是可重入的,以类似的方式工作(将输出参数作为输入传入)。看见http://msdn.microsoft.com/en-us/library/3stkd9be.aspx
至于标准的C++11库为什么不使用这些函数呢?你必须询问编写该规范的人——我希望它具有可移植性和方便性,但我不完全确定。
std::localtime
和std::gmtime
没有线程安全的替代方案,因为您没有提出一个并在整个标准化过程中对其进行封送。其他人也没有。
chrono
唯一的日历代码是包装现有time_t
函数的代码。标准化或编写新的不属于chrono
项目的范围。进行这样的标准化将需要更多的时间、更多的精力,并增加更多的依赖性。简单地包装每个time_t
函数非常简单,几乎没有依赖关系,而且速度很快。
他们的努力范围很窄。他们在专注的领域取得了成功
我鼓励您开始研究<calendar>
或加入这样的努力,为std
创建一个强大的日历API。祝你好运,祝你好运!
如果您愿意使用免费、开源的第三方库,这里有一种在UTC中打印std::chrono::system_clock::time_point
的方法:
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
std::cout << system_clock::now() << " UTCn";
}
这是使用现代C++语法的std::gmtime
的线程安全替代方案。
对于现代的线程安全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";
}
这两者都将以system_clock
支持的任何精度输出,例如:
2016-07-05 10:03:01.608080 EDT
(macOS上为微秒)
这些库远远超出了gmtime
和localtime
的替代品。例如,您想查看儒略历中的当前日期吗?
#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
当前的GPS时间怎么样?
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
std::cout << std::chrono::system_clock::now() << " UTCn";
std::cout << gps_clock::now() << " GPSn";
}
2016-07-05 14:13:02.138091 UTC
2016-07-05 14:13:19.138524 GPS
https://github.com/HowardHinnant/date
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0355r0.html
更新
"date.h"answers"tz.h"库现在已经在C++2a规范草案中了,只做了很小的更改,我们希望"a"是"0"。它们将位于标头<chrono>
和namespace std::chrono
之下(并且不会有date namespace
)。
正如其他人所提到的,在任何可用的C++标准中都没有线程安全的便利性和可移植的时间格式化方法,但我发现有一些过时的预处理器技术是可用的(感谢Andrei Alexandrescu在CppCon 2015幻灯片17&18上的介绍):
std::mutex gmtime_call_mutex;
template< size_t For_Separating_Instantiations >
std::tm const * utc_impl( std::chrono::system_clock::time_point const & tp )
{
thread_local static std::tm tm = {};
std::time_t const time = std::chrono::system_clock::to_time_t( tp );
{
std::unique_lock< std::mutex > ul( gmtime_call_mutex );
tm = *std::gmtime( &time );
}
return &tm;
}
#ifdef __COUNTER__
#define utc( arg ) utc_impl<__COUNTER__>( (arg) )
#else
#define utc( arg ) utc_impl<__LINE__>( (arg) )
#endif
在这里,我们用size_t
模板参数声明函数,并返回指向静态成员std::tm
的指针。现在,每个具有不同模板参数的函数调用都会创建一个具有全新静态std::tm
变量的新函数。如果定义了__COUNTER__
宏,则每次使用时都应将其替换为递增的整数值,否则我们使用__LINE__
宏,在这种情况下,最好确保不会在一行中调用宏utc
两次。
全局gmtime_call_mutex
在每个实例化中保护非线程安全的std::gmtime
调用,至少在Linux中不应该是性能问题,因为获取锁首先是在spinlock周围运行的,在我们的情况下永远不应该以真正的线程锁结束。
thread_local
确保使用utc
调用运行相同代码的不同线程仍将使用不同的std::tm
变量。
用法示例:
void work_with_range(
std::chrono::system_clock::time_point from = {}
, std::chrono::system_clock::time_point to = {}
)
{
std::cout << "Will work with range from "
<< ( from == decltype(from)()
? std::put_time( nullptr, "beginning" )
: std::put_time( utc( from ), "%Y-%m-%d %H:%M:%S" )
)
<< " to "
<< ( to == decltype(to)()
? std::put_time( nullptr, "end" )
: std::put_time( utc( to ), "%Y-%m-%d %H:%M:%S" )
)
<< "."
<< std::endl;
// ...
}
Boost:不完全确定这是否是线程安全的,但似乎是这样:
#include "boost/date_time/posix_time/posix_time.hpp"
std::wstring stamp = boost::posix_time::to_iso_wstring(
boost::posix_time::second_clock::local_time());
std::wstring stamp = boost::posix_time::to_iso_wstring(
boost::posix_time::second_clock::universal_time());
请参阅https://www.boost.org/doc/libs/1_75_0/doc/html/date_time/examples.html
- 使用std::multimap迭代器创建std::list
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如何导出包含具有"std::unique_ptr"值的"std::map"属性的
- 从持续时间构造std::chrono::system_clock::time_point
- std::具有相同基类的类的变体
- std::向量与传递值的动态数组
- 使用std::vector的OpenCL矩阵乘法
- std::map<struct,struct>::find 找不到匹配项,但是如果我循环通过 begin() 到 end(),我在那里看到匹配项
- std::condition_variable::wait()如何评估给定的谓词
- 如何获取std::result_of函数的返回类型
- std::原子加载和存储都需要吗
- 将对象移动到std::shared_ptr
- POCO::PostgreSQL:如何将std::vector支持添加到`Binder::bind`
- 使用一个考虑到std::map中键值的滚动或换行的键
- 如何从 std::atomic 中提取指针 T<T>?
- 为什么 std::unique 不调用 std::sort?
- std::localtime returning null
- 为什么没有C++11线程安全替代std::localtime和std::gmtime