【问题标题】:DateTime.Now causes IO blocking?DateTime.Now 导致 IO 阻塞?
【发布时间】:2012-06-05 14:58:46
【问题描述】:

在一次会议上,有人告诉我DateTime.Now 会导致 IO 阻塞,这是我从未停止考虑的事情。如果有,为什么?

【问题讨论】:

  • 请问你为什么不问告诉你的人?
  • 奇怪为什么这个问题得到如此多的支持而背景信息却如此之少。我的下一个问题:“我听说过多的stackoverflow异常会导致计算机崩溃,如果确实如此,为什么?”
  • 这是一个挑衅性的问题。会议的主题是什么? .Net 一般?视窗手机? XNA?
  • 这是我第一次听说它。例如,MSDN 确实说您不应该使用 DateTime.Now 进行性能测试,但它只是通过说它没有很好的分辨率来解释这一点。
  • 我不确定这是否是 I/O 阻塞的问题,因为它可能会进行系统调用,因此,就像您进行调用 I/O 函数,您就让系统有机会从您的进程中获取控制权并将其交给另一个进程。

标签: c#


【解决方案1】:

好吧,考虑到

mscorelib上使用ILSpy我们可以发现,DateTime.Now是这样出现的:

public static DateTime Now
{
    get
    {
        DateTime utcNow = DateTime.UtcNow;
        bool isAmbiguousDst = false;
        long ticks = TimeZoneInfo.GetDateTimeNowUtcOffsetFromUtc(utcNow, out isAmbiguousDst).Ticks;
        long num = utcNow.Ticks + ticks;
        if (num > 3155378975999999999L)
        {
            return new DateTime(3155378975999999999L, DateTimeKind.Local);
        }
        if (num < 0L)
        {
            return new DateTime(0L, DateTimeKind.Local);
        }
        return new DateTime(num, DateTimeKind.Local, isAmbiguousDst);
    }
}

函数GetDateTimeNowUtcOffsetFromUtc 如下所示:

internal static TimeSpan GetDateTimeNowUtcOffsetFromUtc(DateTime time, out bool isAmbiguousLocalDst)
{
    isAmbiguousLocalDst = false;
    TimeZoneInfo.OffsetAndRule oneYearLocalFromUtc = TimeZoneInfo.GetOneYearLocalFromUtc(time.Year);
    TimeSpan timeSpan = oneYearLocalFromUtc.offset;
    if (oneYearLocalFromUtc.rule != null)
    {
        bool isDaylightSavingsFromUtc = TimeZoneInfo.GetIsDaylightSavingsFromUtc(time, time.Year, oneYearLocalFromUtc.offset, oneYearLocalFromUtc.rule, out isAmbiguousLocalDst);
        timeSpan += (isDaylightSavingsFromUtc ? oneYearLocalFromUtc.rule.DaylightDelta : TimeSpan.Zero);
    }
    return timeSpan;
}

GetOneYearLocalFromUtc 改为:

private static TimeZoneInfo.OffsetAndRule GetOneYearLocalFromUtc(int year)
{
    if (TimeZoneInfo.s_oneYearLocalFromUtc == null || TimeZoneInfo.s_oneYearLocalFromUtc.year != year)
    {
        TimeZoneInfo currentOneYearLocal = TimeZoneInfo.GetCurrentOneYearLocal();
        TimeZoneInfo.AdjustmentRule rule = (currentOneYearLocal.m_adjustmentRules == null) ? null : currentOneYearLocal.m_adjustmentRules[0];
        TimeZoneInfo.s_oneYearLocalFromUtc = new TimeZoneInfo.OffsetAndRule(year, currentOneYearLocal.BaseUtcOffset, rule);
    }
    return TimeZoneInfo.s_oneYearLocalFromUtc;
}

最后GetCurrentOneYearLocal 看起来像:

private static TimeZoneInfo GetCurrentOneYearLocal()
{
    Win32Native.TimeZoneInformation timeZoneInformation = default(Win32Native.TimeZoneInformation);
    long num = (long)UnsafeNativeMethods.GetTimeZoneInformation(out timeZoneInformation);
    TimeZoneInfo result;
    if (num == -1L)
    {
        result = TimeZoneInfo.CreateCustomTimeZone("Local", TimeSpan.Zero, "Local", "Local");
    }
    else
    {
        result = TimeZoneInfo.GetLocalTimeZoneFromWin32Data(timeZoneInformation, false);
    }
    return result;
}

有趣的函数是GetTimeZoneInformation,存在于kernel32.dll,文档中描述如下:

检索当前时区设置。这些设置控制 协调世界时 (UTC) 和本地时间之间的转换。

要访问该时间信息,Windows 实际上使用IO 访问。不确定这个是否可以定义为“阻塞”,但它肯定会访问保存在磁盘上的系统信息,至少是其中的一部分。

【讨论】:

  • 无论如何,对于像DateTime.Now 这样的频繁使用的属性会发生什么令人惊讶。
  • @TimSchmelter:这实际上是像我这样的人只能表达假设的那种问题。因为只有实际在 Windows 操作系统中实现此功能的人(我想,在不同版本之间可以变化)才能给出准确的答案。
  • @TimSchmelter:所以我假设它从磁盘读取,同时考虑其他文档,例如,DYNAMIC_TIME_ZONE,其中信息存储在注册表中,所以也必须从中读取。这并不意味着操作系统可以在启动时兑现它并从内存中读取它(实际上很可能是从某个内存映射文件中读取)。
  • @Tigran:时区信息迟早会被缓存。但是当前时间是从南桥芯片(或等效的)中检索的,这需要阻塞 I/O。所以真正有趣的部分不在 .NET 代码中,而是在 GetTimeZoneInformation
  • @Codo:这实际上就是我的答案的全部内容:)
【解决方案2】:

到目前为止,没有人回答当前时间的真正来源。我不了解最新的 PC 架构。但几年前,real-time clock 是 CPU(南桥)外部芯片的一部分。因此,为了获得时间,您必须对该芯片进行一些 I/O 操作。 (这不是磁盘访问,而是 I/O 操作。)

而且由于当前进程必须等待时钟的响应,所以它阻塞了 I/O。

所以会议上的那个人是对的。

【讨论】:

    【解决方案3】:

    如果您使用诸如工具 DateTime.Now 之类的反射器查看源代码,则调用 Win API GetSystemTimeAsFileTime,然后它使用 constructor 创建新的 DateTime 对象,该对象具有一个 int64 参数,自 0001 年 1 月 1 日起在 00 处出现滴答声: 00:00.000。此处看不到任何可能导致 I/O 阻塞的内容,GetSystemTimeAsFileTime 文档中也没有提及。

    【讨论】:

    • 我猜你错过了当前时间的真正来源:南桥芯片。要访问它,需要 I/O 并且它是阻塞的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-03
    • 2010-11-17
    • 1970-01-01
    • 1970-01-01
    • 2017-09-18
    • 1970-01-01
    • 2012-02-07
    相关资源
    最近更新 更多