时区规则的 Linux/跨平台 API?(替换锁定localtime_r)
Linux/crossplatform API for timezone rules? (replace locking localtime_r)
我们有一些代码想要经常从多个线程调用localtime
。(相关背景:它是一个服务器,你可以要求它提供本地时间作为字符串,它希望每秒能够处理 100K 的请求。
我们发现在 Ubuntu Linux 12.04 上,glibc 函数localtime_r
("可重入本地时间")调用 __tz_convert
,它仍然需要全局锁定!
(另外,看起来 FreeBSD 在每次调用时都会localtime_r
调用tzset
, 因为他们偏执地认为程序可能已经做了一个setenv("TZ")
和/或用户从现在到上次调用localtime_r
之间下载了新版本的/etc/localtime
。(这与这里描述的情况相反;似乎glibc在每次调用localtime
时都会调用tzset
,而不是localtime_r
,只是为了混淆。
显然,这对性能来说是可怕的。出于我们的目的,我们希望在服务器开始运行时基本上"快照"当前时区的规则,然后永久使用该快照。因此,我们将继续遵守夏令时规则(因为何时切换到 DST 的规则将是快照的一部分),但我们永远不会返回磁盘、使用互斥锁或执行任何其他会导致线程阻塞的操作。(我们不尊重下载的 tzinfo 更新,也不尊重对/etc/localtime
的更改;我们不希望服务器在运行时物理更改时区。
但是,我在网上找不到任何关于如何处理时区规则的信息——是否有使用它们的用户空间 API,或者我们是否会被迫重新实现几百行 glibc 代码来自己读取时区数据。
我们是否必须重新实现__tz_convert
下游的所有内容——包括 tzfile_read
,因为它似乎没有暴露给用户?或者是否有一些POSIX接口和/或第三方库可用于处理时区规则?
(我看过 http://www.iana.org/time-zones/repository/tz-link.html 但我不确定它是否有帮助。
使用 https://github.com/google/cctz
它速度很快,应该使用非常简单的 API 完成您想要的一切。
特别是,对于等效于 localtime
的 cctz,请使用 cctz::BreakTime()
函数。例如,https://github.com/google/cctz/blob/master/examples/example3.cc
也许这个免费的开源时区库可以满足需求。
它有一个名为 LAZY_INIT
的配置标志,此处已完整记录。 默认情况下,它处于打开状态,并且将导致调用在首次访问每个单独的时区时std::call_once
。 但是,您可以使用以下方法进行编译:
-DLAZY_INIT=0
然后std::call_once
的电话就消失了。 每个时区都是从磁盘读取的,并在首次访问时完全初始化(通过函数本地静态)。 从那时起,事情变得稳定且无锁,无需磁盘访问。 当然,这会增加前期的初始化时间,但会减少每个时区的"首次访问"时间。
此库需要 C++11/14,因此可能不适合该原因。 它基于 C++11 <chrono>
库(并大量使用)。 下面是打印当前本地时间的示例代码:
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
auto local = make_zoned(current_zone(), std::chrono::system_clock::now());
std::cout << local << 'n';
}
只是为我输出:
2016-04-12 10:13:14.585945 EDT
该库采用现代高性能线程安全设计。 它也非常灵活且有完整的文档记录。 它的功能远远超出了对C localtime
的简单替代品。 与 C API 不同,您可以指定所需的任何 IANA 时区,例如:
auto local = make_zoned("Europe/London", std::chrono::system_clock::now());
这给出了伦敦的当前时间:
2016-04-12 15:19:59.035533 BST
请注意,默认情况下,时间戳的精度为 std::chrono::system_clock
。 如果您更喜欢其他精度,则很容易实现:
using namespace date;
using namespace std::chrono;
auto local = make_zoned("Europe/London", std::chrono::system_clock::now());
std::cout << format("%F %H:%M %Z", local) << 'n';
2016-04-12 15:22 BST
有关更多详细信息,请参阅文档。
- 模板参数替换失败,并且未完成隐式转换
- 如何找到锁定Linux futex的C++行
- G锁定铸造到基础上会释放模拟行为
- 如何用转义符替换字符串中的所有特殊字符
- 如何检查线程是否锁定
- 为什么除非添加括号,否则构造函数上的模板替换会失败?
- 如何在C++中找到active directory中禁用和锁定的窗口帐户
- 我应该在锁定TBitmap画布后解锁它吗
- 在一个读写器队列中,我可以用volatile替换原子吗
- 用符号版本替换对函数的所有调用
- 如何通过替换顺序代码的while循环来添加OpenMP for循环
- 替换基于地图的所有引用
- 按平均值替换数组中的元素
- 我可以在这里替换什么,因为我不能在 C# 中使用隐式变量的 lambda 函数?
- 如何将字节数组元素替换为修改的十六进制 ASCII 符号?
- 初始化 std::vector 替换为单大括号而不是双大括号
- 删除/替换C++字符串中的多字符 (ÿû)
- 将 malloc 替换为数组
- 如何替换此示例代码片段中已弃用的handler_type_t或 boost::asio::handler_type?
- 时区规则的 Linux/跨平台 API?(替换锁定localtime_r)