patterncsharpMinor
Minimalist countdown timer with C#
Viewed 0 times
countdownwithtimerminimalist
Problem
I'm looking to add a module to one of my utility classes that functions as a countdown timer for tools that execute tasks after a certain amount of time. The code below is what I have working for PoC, but when I went to make this and check out existing examples, none really seemed this slim/simple without going all-out on lambdas, so I'm worried that I'm doing something wrong, or at the very least very inefficiently.
The time-range expected for the tool will be from 200 milliseconds to a few hours.
I also have this alternative when I was futzing around with async, which I'm kind of new to in C#
```
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace CountdownTimer
{
class Program
{
static int Main(string[] args)
{
var result = CountDownTimer(5000);
Console.ReadKey(true);
return 0;
}
async static Task CountDownTimer(int delay)
{
Task myDelay = AsyncDelay(delay);
await myDelay;
}
async static Task AsyncDelay(int delay)
{
await Task.Delay(TimeSpan.FromMilliseconds(delay));
Console.WriteLine("Done");
The time-range expected for the tool will be from 200 milliseconds to a few hours.
using System;
namespace CountdownTimer
{
class Program
{
static int Main(string[] args)
{
int aDelay = 2000;
CountDown(aDelay);
return 0;
}
static void CountDown(int delayInMilliseconds)
{
bool finishedCountdown = false;
DateTime initialTime = DateTime.UtcNow;
while (!finishedCountdown)
{
DateTime timeNow = DateTime.UtcNow;
TimeSpan timeDifference = timeNow - initialTime;
if (timeDifference.TotalMilliseconds >= delayInMilliseconds)
{
finishedCountdown = true;
}
}
}
}
}I also have this alternative when I was futzing around with async, which I'm kind of new to in C#
```
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace CountdownTimer
{
class Program
{
static int Main(string[] args)
{
var result = CountDownTimer(5000);
Console.ReadKey(true);
return 0;
}
async static Task CountDownTimer(int delay)
{
Task myDelay = AsyncDelay(delay);
await myDelay;
}
async static Task AsyncDelay(int delay)
{
await Task.Delay(TimeSpan.FromMilliseconds(delay));
Console.WriteLine("Done");
Solution
Without more context it's really hard to give you a particularly helpful answer but I can suggest a couple of things.
Your first option is about as bad as it gets, it will thrash CPU because as far as the CPU is concerned that while loop is important work - it doesn't know you're just calculating a time over and over again.
Your second option isn't bad but you should accept an action to run after the delay otherwise it won't be very flexible. You should also use
For the sake of completeness, for this trivial example I think it's clearer without the
As you're returning a
If you want to be able to cancel your action you should feed a
Update
To actually use the method:
Your first option is about as bad as it gets, it will thrash CPU because as far as the CPU is concerned that while loop is important work - it doesn't know you're just calculating a time over and over again.
Your second option isn't bad but you should accept an action to run after the delay otherwise it won't be very flexible. You should also use
TimeSpan to represent an interval - an int can mean many things, a TimeSpan represents the information unambiguously.For the sake of completeness, for this trivial example I think it's clearer without the
async and await but that's just personal preference.public static Task DoActionAfter(TimeSpan delay, Action action)
{
return Task.Delay(delay).ContinueWith(_ => action());
}As you're returning a
Task your code can synchronously wait using .Wait() (not great) or await the result. If it doesn't need the result straight away it can just store the task and check its status later.If you want to be able to cancel your action you should feed a
CancellationToken into the method (and the delay).Update
To actually use the method:
using System;
using System.Threading.Tasks;
namespace CountdownTimer
{
class Program
{
public static void Main()
{
var delayInterval = TimeSpan.FromMilliseconds(5000);
var runningTask = DoActionAfter(
delayInterval,
() => Console.WriteLine("Done"));
// if you want your main method to wait for the action
// runningTask.Wait()
Console.ReadKey();
}
public static Task DoActionAfter(TimeSpan delay, Action action)
{
return Task.Delay(delay).ContinueWith(_ => action());
}
}
}Code Snippets
public static Task DoActionAfter(TimeSpan delay, Action action)
{
return Task.Delay(delay).ContinueWith(_ => action());
}using System;
using System.Threading.Tasks;
namespace CountdownTimer
{
class Program
{
public static void Main()
{
var delayInterval = TimeSpan.FromMilliseconds(5000);
var runningTask = DoActionAfter(
delayInterval,
() => Console.WriteLine("Done"));
// if you want your main method to wait for the action
// runningTask.Wait()
Console.ReadKey();
}
public static Task DoActionAfter(TimeSpan delay, Action action)
{
return Task.Delay(delay).ContinueWith(_ => action());
}
}
}Context
StackExchange Code Review Q#116517, answer score: 4
Revisions (0)
No revisions yet.