c# 高并发下的 TimeZoneInfo 缓存和性能问题

轻敏吖_2538

轻敏吖_2538

2026-01-08

225人浏览

原创

务必在应用启动时预热 TimeZoneInfo.FindSystemTimeZoneById,避免高并发下因首次解析时区数据库和锁竞争导致 5–20ms/次延迟;ID 大小写敏感,IANA 时区如 "Etc/GMT+8" 需用 CreateCustomTimeZone 构造并预缓存;慎用 TimeZoneInfo.Local,容器中需显式设 TZ 环境变量。

c# 高并发下的 timezoneinfo 缓存和性能问题

TimeZoneInfo.FindSystemTimeZoneById 在高并发下会变慢

TimeZoneInfo.FindSystemTimeZoneById 内部不是纯内存查找,它在首次调用时会触发系统时区数据库的解析(Windows 上读注册表或 ICU 数据,Linux/macOS 依赖 /usr/share/zoneinfo 文件结构),后续调用虽有缓存,但该缓存受内部 ConcurrentDictionary 保护,且存在锁竞争和键规范化开销。实测在 10K+ QPS 场景下,未预热时平均耗时可飙升至 5–20ms/次。

  • 务必在应用启动时主动调用一次 TimeZoneInfo.FindSystemTimeZoneById("UTC") 或常用 ID(如 "China Standard Time")完成初始化
  • 避免在请求处理路径中直接调用 FindSystemTimeZoneById —— 改为从预加载字典中取值:
    private static readonly ConcurrentDictionary<string timezoneinfo> _tzCache = new();
    static MyService()
    {
        foreach (var id in new[] { "UTC", "China Standard Time", "Pacific Standard Time" })
        {
            _tzCache[id] = TimeZoneInfo.FindSystemTimeZoneById(id);
        }
    }</string>
  • 注意:ID 是大小写敏感的,"utc" 会抛 TimeZoneNotFoundException

自定义时区(如 Etc/GMT+8)不能用 FindSystemTimeZoneById 直接获取

像 "Etc/GMT+8" 这类 IANA 时区名在 Windows 上默认不可用,FindSystemTimeZoneById 会直接抛异常;.NET 6+ 虽支持 IANA 数据(需启用 AppContext.SetSwitch("System.Globalization.UseNls", false)),但该开关是进程级的,且切换后会影响所有 DateTimeFormatInfo 行为,不建议 runtime 动态开启。

  • 若必须支持 IANA 时区,改用 TimeZoneInfo.CreateCustomTimeZone 构造固定偏移时区:
    var gmtPlus8 = TimeZoneInfo.CreateCustomTimeZone("GMT+8", TimeSpan.FromHours(8), "GMT+8", "GMT+8");
  • 不要在每次请求中重复创建 —— 同样应预缓存到 ConcurrentDictionary 或 static readonly 字段
  • 注意:CreateCustomTimeZone 不支持夏令时,如需 DST,请改用第三方库(如 NodaTime)

TimeZoneInfo.ConvertTime 的线程安全性与性能陷阱

TimeZoneInfo.ConvertTime 本身是线程安全的,但它的性能取决于两个因素:目标时区是否已“热”、转换逻辑是否涉及复杂规则(如历史 DST 变更)。对非本地时区做频繁转换(例如日志时间戳转用户本地时间),若未复用 TimeZoneInfo 实例,会反复触发内部时区规则解析。

Grok Build 源码精华:压缩/重试/崩溃恢复/钩子/扩展注册表
Grok Build 源码精华:压缩/重试/崩溃恢复/钩子/扩展注册表

从 grok-build 125万行 Rust 源码深读后移植的 5 个独立 Python 模块:上下文压缩引擎、推理重试退避、崩溃恢复、钩子运行时、扩展注册表。每个模块可独立 import 或 CLI 调用。

下载
  • 禁止这样写:
    var tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
    var local = TimeZoneInfo.ConvertTime(utcTime, tz, TimeZoneInfo.Local); // 每次都 new tz?不,但 FindSystemTimeZoneById 被反复调用就糟了
  • 正确做法是:把 TimeZoneInfo 实例作为字段或参数传入,确保复用
  • 如果转换目标固定(如全站统一转成 "Asia/Shanghai"),直接缓存该实例,别每次都查
  • 极端吞吐场景下,考虑用 DateTimeOffset 替代 —— 若只需偏移量而无需时区名称或 DST 规则,DateTimeOffset 零分配、无查找开销

跨平台部署时 TimeZoneInfo.Local 的行为差异

TimeZoneInfo.Local 在 Linux/macOS 上依赖 TZ 环境变量或 /etc/localtime 符号链接,在容器中极易为空或指向错误时区(比如 Alpine 镜像默认没设 TZ)。此时 Local 可能回退为 UTC,且首次访问会触发同步锁,造成毛刺。

  • Dockerfile 中显式设置时区:
    ENV TZ=Asia/Shanghai
    RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
  • 代码中不要假设 TimeZoneInfo.Local 总是可用 —— 加一层 fallback:
    private static readonly TimeZoneInfo _appDefaultTz = 
        TimeZoneInfo.FindSystemTimeZoneById("China Standard Time");
    public static TimeZoneInfo GetEffectiveTimeZone(TimeZoneInfo? userTz = null) =>
        userTz ?? TimeZoneInfo.Local ?? _appDefaultTz;
  • 注意:TimeZoneInfo.Local 是只读属性,但其内部缓存可能被 ClearCachedData() 清空(极少用,慎调)

缓存策略本身不难,真正容易出问题的是“以为它已经缓存了”——比如漏掉预热、混用大小写 ID、或在容器里忘了配 TZ。这些点一旦在线上高频路径触发,表现就是 CPU 突增 + 请求延迟抖动,排查时容易误判为 GC 或锁竞争。

相关文章

数码产品性能查询
数码产品性能查询

该软件包括了市面上所有手机CPU,手机跑分情况,电脑CPU,电脑产品信息等等,方便需要大家查阅数码产品最新情况,了解产品特性,能够进行对比选择最具性价比的商品。

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

2023.08.10

4898

6

windows查看端口占用情况
windows查看端口占用情况

Windows端口可以认为是计算机与外界通讯交流的出入口。逻辑意义上的端口一般是指TCP/IP协议中的端口,端口号的范围从0到65535,比如用于浏览网页服务的80端口,用于FTP服务的21端口等等。怎么查看windows端口占用情况呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.07.26

3499

3

查看端口占用情况windows
查看端口占用情况windows

端口占用是指与端口关联的软件占用端口而使得其他应用程序无法使用这些端口,端口占用问题是计算机系统编程领域的一个常见问题,端口占用的根本原因可能是操作系统的一些错误,服务器也可能会出现端口占用问题。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.07.27

3038

6

windows照片无法显示
windows照片无法显示

当我们尝试打开一张图片时,可能会出现一个错误提示,提示说"Windows照片查看器无法显示此图片,因为计算机上的可用内存不足",本专题为大家提供windows照片无法显示相关的文章,帮助大家解决该问题。

2023.08.01

1548

3

windows查看端口被占用的情况
windows查看端口被占用的情况

windows查看端口被占用的情况的方法:1、使用Windows自带的资源监视器;2、使用命令提示符查看端口信息;3、使用任务管理器查看占用端口的进程。本专题为大家提供windows查看端口被占用的情况的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

2759

5

windows无法访问共享电脑
windows无法访问共享电脑

在现代社会中,共享电脑是办公室和家庭的重要组成部分。然而,有时我们可能会遇到Windows无法访问共享电脑的问题。这个问题可能会导致数据无法共享,影响工作和生活的正常进行。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.08

3370

4

windows自动更新
windows自动更新

Windows操作系统的自动更新功能可以确保系统及时获取最新的补丁和安全更新,以提高系统的稳定性和安全性。然而,有时候我们可能希望暂时或永久地关闭Windows的自动更新功能。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.10

1757

3

windows boot manager
windows boot manager

windows boot manager无法开机的解决方法:1、系统文件损坏,使用Windows安装光盘或USB启动盘进入恢复环境,选择修复计算机,然后选择自动修复;2、引导顺序错误,进入恢复环境,选择命令提示符,输入命令"bootrec /fixboot"和"bootrec /fixmbr",然后重新启动计算机;3、硬件问题,使用硬盘检测工具进行扫描和修复;4、重装操作系统。本专题还提供其他解决

2023.08.28

2537

4

windows锁屏快捷键
windows锁屏快捷键

windows锁屏快捷键是Windows键+L、Ctrl+Alt+Del、Windows键+D、Windows键+P和Windows键+R。本专题为大家提供windows相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.30

5547

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Python uv官方安装与入门文档
Python uv官方安装与入门文档

共0课时 | 0人学习

Buffalo框架快速入门指南
Buffalo框架快速入门指南

共0课时 | 0人学习

Valgrind支持平台说明
Valgrind支持平台说明

共0课时 | 0人学习