我在这里遇到一种奇怪的行为。控制永远不会来自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();
}
答案 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.Stopwatch
。 Stopwatch
类工作正常,并且确实在我使用的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...
// }
}