【问题标题】:Get current time on time change获取当前时间的时间变化
【发布时间】:2017-07-09 17:56:20
【问题描述】:

我有以下代码来显示当前时间:

static void Main(string[] args)
{
    while (true)
    {
        ShowDate(GetTime(),GetDate());
        //Here's my issue:
        System.Threading.Thread.Sleep(1000);
    }
}

//Clear Console and write current Date again.
static void ShowDate(String time, String date)
{
    Console.Clear();
    Console.WriteLine(date);
    Console.WriteLine(time);
}
static string GetTime()
{
    string timeNow = DateTime.Now.ToString("HH:mm:ss");
    return timeNow;
}
static string GetDate()
{
    string dateNow = DateTime.Now.ToString("dd.MM.yy");
    return dateNow;
}

嗯,据我了解,Thread.Sleep(1000) 仅显示程序启动后每秒测量的时间。所以它没有显示完全正确的时间。我可以为Thread.Sleep() 尝试一个较低的值,但这仍然有点不准确,并且可能变得有点低效。 有没有可能的方法,例如,每次系统本身更新时间时更新时间?可能是event listener 之类的东西?

【问题讨论】:

  • “有点不准确”是什么意思?
  • 你需要完成什么?
  • 你的问题在于睡眠 1000,加上运行一些代码并不总是正确的。sleep(1000) 不能保证为 1000,它大约 1000 ..另外,假设你从 995 开始那 1000 秒,您将始终停留在 995 毫秒。
  • 为什么需要这么精确?显示的代码足以满足 99% 的用例
  • 你熟悉Nyquist–Shannon sampling theorem吗?针对这个特殊情况稍微解释一下:定义最小采样率是多少,你会认为它“足够精确”,然后使用两倍。

标签: c# multithreading timer clock


【解决方案1】:

不要使用 Thread.Sleep 来停止线程执行,我认为使用传统的 Timer 会更好:

static void Main(string[] args)
{
    System.Timers.Timer timer = new System.Timers.Timer(1000);
    timer.Elapsed += Tim_Elapsed;
    timer.Start();
}

private void Tim_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    ShowDate();
}

【讨论】:

  • 好的,谢谢。定时器更准确吧?并且不中断当前线程?
  • System.Timers.Timer(不要与其他 Timer 类混淆)在系统线程池上运行,因此它不会阻塞您的主线程。它总是在您声明的已用时间内触发,从调用 Start() 方法的特定时间开始。示例:如果您在 22:31:15.332 开始该方法,它将在 22:31:16.332 再次运行,而不是在 22:31:16.000
【解决方案2】:

正如您所写,这将每秒刷新(写入)时间 - 0、1、2、3 .. 但是如果程序在某个中间时间开始,它会像 1.3, 2.3, 3.3

这并不总是预期的行为,但每次更改的 Event 也会很消耗 - 您可能知道存在一些“系统时间”,这些“系统时间”计入 Ticks

每次处理器 jumps 到下一条指令时都会发生滴答声,因为它有一些 frequency,它可以从中重新计算当前时间。

但是.NET 允许您使用一些预构建Timers,它可以以毫秒的精度运行。

示例代码as in here:

using System;
using System.Threading;

public static class Program
{
    public static void Main()
    {
        // Create a Timer object that knows to call our TimerCallback
        // method once every 2000 milliseconds.
        Timer t = new Timer(TimerCallback, null, 0, 2000);
        // Wait for the user to hit <Enter>
        Console.ReadLine();
    }

    private static void TimerCallback(Object o)
    {
        // Display the date/time when this method got called.
        Console.WriteLine("In TimerCallback: " + DateTime.Now);
        // Force a garbage collection to occur for this demo.
        GC.Collect();
    }
}

重要提示:您正在使用Thread.Sleep(),这将导致Thread 停止其所有工作,这确实是延迟某些活动的不充分方式,应尽量减少使用。有一些特殊的场合,确实有用,但肯定不是这个。

【讨论】:

    【解决方案3】:

    我会使用每秒发出事件的自定义时钟类来解决这个问题。通过测量下一秒到期之前的剩余时间,我们可以等到那一刻,然后触发一个事件。

    利用async/await 带来代码清晰的好处,利用IDisposable 进行清理,这可能看起来像这样:

    void Main()
    {
        using(var clock = new Clock())
        {
            clock.Tick += dt => Console.WriteLine(dt);
            Thread.Sleep(20000);
    
        }
    
    }
    //sealed so we don't need to bother with full IDisposable pattern
    public sealed class Clock:IDisposable
    {
        public event Action<DateTime> Tick;
        private CancellationTokenSource tokenSource;
        public Clock()
        {
            tokenSource = new CancellationTokenSource();
            Loop();
        }
        private async void Loop()
        {
            while(!tokenSource.IsCancellationRequested)
            {
                var now = DateTime.UtcNow;
                var nowMs = now.Millisecond;
                var timeToNextSecInMs = 1000 - nowMs;
                try
                {
                    await Task.Delay(timeToNextSecInMs, tokenSource.Token);
    
                }
                catch(TaskCanceledException)
                {
                    break;
                }
                var tick = Tick;
                if(tick != null)
                {
                    tick(DateTime.UtcNow);
                }
            }
    
        }
        public void Dispose()
        {
            tokenSource.Cancel();
        }
    }
    

    【讨论】:

    • 我喜欢这个答案,因为游戏中使用类似的系统来刷新某些特定的帧速率。
    猜你喜欢
    • 1970-01-01
    • 2012-03-04
    • 1970-01-01
    • 2013-03-10
    • 1970-01-01
    • 2010-10-05
    • 2021-12-04
    • 1970-01-01
    相关资源
    最近更新 更多