如何使用DateTime类测量经过的时间?

时间:2017-08-30 12:04:17

标签: c# time

我在这里遇到一种奇怪的行为。控制永远不会来自do-while循环。在调试时,我发现变量间隔的值在某些循环期间减少,而不是增加!我在这里遗漏了一些明显的东西,但无法弄明白。

static void Main(string[] args)
{
    int interval = 0;
    DateTime startTime = DateTime.Now;

    //Task1(); //Task1 can take 4-8 seconds to complete

    do
    {
        Thread.Sleep(100);
        interval = (DateTime.Now - startTime).Milliseconds;
    } while (interval < 10000); //wait for at-least ten seconds from program start, before executing task2

    //Task2();
}

4 个答案:

答案 0 :(得分:12)

请勿使用DateTime来衡量时间间隔。

使用专为此目的设计的Stopwatch

var clock = new Stopwatch();
clock.Start();
do
{
    // do something
}
while (clock.ElapsedMilliseconds < 10000)
clock.Stop();

注意:当然您可以使用DateTime来测量时间,但如果您需要精度小于秒,那么Stopwatch就是工作的正确工具。
并且Stopwatch具有更易读且易于使用的方法来衡量时间

在您的特定情况下:“等待程序启动至少十秒,执行task2之前”您可以使用异步方法

var task1 = StartTask1();

await Task.Delay(10000); // next line will be executed approximately after 10 s

var task2 = StartTask2();

答案 1 :(得分:9)

您的问题是您测量TimeSpan的毫秒部分。您必须使用TotalMilliseconds代替Milliseconds

do
{
    Thread.Sleep(100);
    interval = (DateTime.Now - startTime).TotalMilliseconds;
} while (interval < 10000); //wait for at-least ten seconds from program start, before executing task2

答案 2 :(得分:2)

使用TimeSpan,如果您想知道已经过去的时间,则必须始终使用Total属性。在其前面没有Total的所有属性仅显示已经过去的当前金额

例如:一种方法运行1分7秒。

Console.WriteLine(ts.Seconds);

输出:

  

7

为什么?因为一分钟内只有60秒,ts.Seconds将从0开始再次增加到59.

将此与

进行比较
Console.WriteLine(ts.TotalSeconds),

输出:

  

67

现在我们已经过了总秒数,在这种情况下是67秒。

答案 3 :(得分:1)

我创建了以下结构作为易于使用的计时器,以替代System.Diagnostics.StopwatchStopwatch类工作正常,并且确实在我使用的Windows系统上提供了高精度的间隔检查。不过,在某些情况下,我更喜欢EasyTimer提供的简单API,而不是必须启动/重新启动/停止Stopwatch。当我不需要间隔时间非常准确时,尤其是在球场上时,尤其如此。作为一种值类型,它不会对垃圾收集器施加任何压力,并且使用EasyTimer检查间隔的效率(作为微优化)要比使用Stopwatch的效率高一些。

public struct EasyTimer
{
    /// <summary>
    /// Gets a value indicating whether or not the time interval has elapsed.
    /// </summary>
    /// <remarks>
    /// If the time interval has elapsed, the timer is reset to check the next time interval.
    /// </remarks>
    public bool HasElapsed
    {
        get
        {
            var nowTicks = DateTime.UtcNow.Ticks;
            if (nowTicks - this.startTicks < this.intervalTicks) { return false; }
            this.startTicks = DateTime.UtcNow.Ticks;
            return true;
        }
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="EasyTimer"/> structure with the
    /// time interval to check.
    /// </summary>
    /// <param name="intervalInMilliseconds">
    /// The time interval to check in milliseconds.
    /// </param>
    public EasyTimer(int intervalInMilliseconds)
    {
        this.intervalTicks = TimeSpan.FromMilliseconds(intervalInMilliseconds).Ticks;
        this.startTicks    = DateTime.UtcNow.Ticks;
    }

    private          long startTicks;
    private readonly long intervalTicks;
}

这是我使用它的一个例子。考虑...

using System.Threading;
private ManualResetEvent shutdownEvent = new ManualResetEvent(false);

// Allow external user to signal work to stop.
public void Shutdown()
{
    this.shutdownEvent.Set();
}

private void DoWork()
{
    // This approach requires checking the event each time through the loop,
    // which involves overhead I wish to minimize.
    while (!this.shutdownEvent.WaitOne(0))
    {
        // Do some ongoing work here...
    }
}

这是一种使用Stopwatch来使开销最小化的方法,但是请注意额外的复杂性。

using System.Threading;
private ManualResetEvent shutdownEvent = new ManualResetEvent(false);

// Allow external user to signal work to stop.
public void Shutdown()
{
    this.shutdownEvent.Set();
}

private void DoWork()
{
    var sw = Stopwatch.StartNew();
    while (true)
    {
       // Do some ongoing work here...

       // Check the ShutdownEvent every second.
       if (sw.ElapsedMilliseconds >= 1000)
       {
           if (this.shutdownEvent.WaitOne(0)) { break; }
           sw.Restart();
       }
    }
}

最后,这是使用EasyTimer的方法。

using System.Threading;
private ManualResetEvent shutdownEvent = new ManualResetEvent(false);

// Allow external user to signal work to stop.
public void Shutdown()
{
    this.shutdownEvent.Set();
}

private void DoWork()
{
    var et = new EasyTimer(1000);
    while (true)
    {
       // Do some ongoing work here...

       // Check the ShutdownEvent every second (or so).
       if (et.HasElapsed && this.shutdown.WaitOne(0)) { break; }
    }

    // or alternatively...
    // while (!(et.HasElapsed && this.shutdownEvent.WaitOne(0))
    // {
    //     // Do some ongoing work here...
    // }
}