Reputation: 3472
I am encountering a strange behavior here. Control never comes out of the do-while loop. On debugging i found that the value of the variable interval decreases during some of the loops, instead of increasing! I'm missing something obvious here, but can't figure it out.
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();
}
Upvotes: 8
Views: 12420
Reputation: 46034
I created the following struct as a simple-to-use timer as an alternative to System.Diagnostics.Stopwatch
. The Stopwatch
class works just fine and truly does provide high-precision interval checking on the Windows systems I use. In some cases, though, I prefer the simpler API offered by EasyTimer
over having to start/restart/stop the Stopwatch
. This is especially true when I don't need to be highly accurate with the interval, just in-the-ballpark. As a value type, it doesn't add any pressure to the garbage collector, and it's slightly more efficient (as a micro optimization) to check the interval with EasyTimer
than it is with 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;
}
Here's an example of what I use it for. Consider...
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...
}
}
Here's an approach that minimizes the overhead using Stopwatch
, but note the additional complexity.
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();
}
}
}
Finally, here's an approach using 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...
// }
}
Upvotes: 3
Reputation: 32445
Don't use DateTime
for measuring time intervals.
Use Stopwatch
class, which was designed exactly for this purpose
var clock = new Stopwatch();
clock.Start();
do
{
// do something
}
while (clock.ElapsedMilliseconds < 10000)
clock.Stop();
Note: Of course you can use DateTime
for measuring time, but if you need precision less then second then Stopwatch
is right tool for the job.
And Stopwatch
have much more readable and easy to use methods for measuring time
In your particular case: "wait for at-least ten seconds from program start, before executing task2" you can use asynchronous approach
var task1 = StartTask1();
await Task.Delay(10000); // next line will be executed approximately after 10 s
var task2 = StartTask2();
Upvotes: 16
Reputation: 3407
With TimeSpan
you always have to use the Total
properties if you want to know the total time that has passed. All properties without Total
in front of them only display the current amount of time that has passed.
For example: A method runs for 1 minute and 7 seconds.
Console.WriteLine(ts.Seconds);
Output:
7
Why is that? Because there are only 60 seconds in a minute and ts.Seconds
will start increasing again from 0 to 59.
Compare this to
Console.WriteLine(ts.TotalSeconds),
Output:
67
Now we have the total amount of seconds that have passed, in this case 67 seconds.
Upvotes: 3
Reputation: 14007
Your problem is that you measure the milliseconds part of the TimeSpan
. You have to use TotalMilliseconds
instead of 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
Upvotes: 12