C# Task.Delay wait period before starting - c#

Was wondering if there was a way to have Task.Delay() wait before a certain timeperiod (e.g. minutes) before starting the wait interval? I was thinking something similar to Timer where you can specify a "dueTime" before it actually starts. So something like Task.Delay(5, 10, cancellationToken), where it will delay every 10 minutes but starting the count 5 minutes later.

Task.Delay isn't periodic to begin with. One Task, one wakeup.
Are you calling it in a loop? Call it once before entering the loop, with the alternate delay.
e.g. change
while (true) {
await Task.Delay(N);
DoStuff();
}
to
await Task.Delay(X);
while (true) {
DoStuff();
await Task.Delay(Y);
}

Related

threads, tasks, states, need help picking the right one

I need some help in choosing the right tool. I'm replacing the hardware controller that controls some pumps with a raspberry pi and writing code for it in c# .netcore. The pumps should run in a specific sequence and for a specified duration. With all the possible ways to accomplish this, I'm looking for the cleanest and interesting one.
The pumps should do the following:
Turn on pump 1
wait 15 seconds
turn on pump 2
wait 10 minutes
turn on pump 3
let pump 3 run for 20 minutes
turn off pump 3
wait 10 minutes
turn off pump 2
wait 15 seconds
turn off pump 1
I looked into timers, threads, tasks, state machine but I have a hard time picking the right tool for this job. At all times, I also need to be able to stop immediately all pumps.
Thanks for your help.
I'd probably go with tasks.
public async Task Execute()
{
await TurnOnPump1();
await Task.Delay(TimeSpan.FromSeconds(15));
await TurnOnPump2();
await Task.Delay(TimeSpan.FromMinutes(10));
await TurnOnPump3();
//And so on..
}
To expand on the great answer from Magnus, here's how you could implement cancellation so you could stop executing the method (stop starting new pumps) if you decide to stop all of them.
I posted this answer because OP specifically said that they need to be able to stop the pumps at all times, so Magnus' answer wouldn't quite work in certain scenarios.
At all times, I also need to be able to stop immediately all pumps.
public async Task StartAll(CancellationToken ct)
{
await TurnOnPump1(); // no ct here because these methods should take little to no time to execute
await Task.Delay(TimeSpan.FromSeconds(15), ct);
await TurnOnPump2();
await Task.Delay(TimeSpan.FromMinutes(10), ct);
await TurnOnPump3();
//And so on..
}
public asnyc Task StopAll()
{
// Your_CancellationTokenSource should be defined somewhere else
Your_CancellationTokenSource.Cancel(); // this line makes Task.Delay throw a TaskCanceledException
await StopPump1();
await StopPump2();
await StopPump3();
// ..
}
public async Task HowToCallStart()
{
try
{
// Your_CancellationTokenSource should be defined somewhere else
await StartAll(Your_CancellationTokenSource.Token);
}
catch (TaskCanceledException)
{
// Starting was canceled
}
}
This way, StopAll can be called anytime during the starting and you don't get any issues.
A few things to mention:
Your_CancellationTokenSource should of course be some variable outside of these methods so it can be shared. It needs to be of type CancellationTokenSource.
As you can see by the comment (both in code and below answer), I assumed that starting a pump would be very fast and take very little to no time. That is the reason I did not pass in my CancellationToken.
If turning on the pumps takes some time, consider using CancellationToken inside the TurnOnPumpX methods as well to abort if the operation was canceled. If you do so, you can simply pass in ct to those methods as well.
You should add some code in the catch for when the operation is canceled. At least print out a debug message if the end-user doesn't need to see it.

Effects of non-awaited Task

I have a Task which I do not await because I want it to continue its own logic in the background. Part of that logic is to delay 60 seconds and check back in to see if some minute work is to be done. The abbreviate code looks something like this:
public Dictionary<string, Task> taskQueue = new Dictionary<string, Task>();
// Entry point
public void DoMainWork(string workId, XmlDocument workInstructions){
// A work task (i.e. "workInstructions") is actually a plugin which might use its own tasks internally or any other logic it sees fit.
var workTask = Task.Factory.StartNew(() => {
// Main work code that interprets workInstructions
// .........
// .........
// etc.
}, TaskCreationOptions.LongRunning);
// Add the work task to the queue of currently running tasks
taskQueue.Add(workId, workTask);
// Delay a period of time and then see if we need to extend our timeout for doing main work code
this.QueueCheckinOnWorkTask(workId); // Note the non-awaited task
}
private async Task QueueCheckinOnWorkTask(string workId){
DateTime startTime = DateTime.Now;
// Delay 60 seconds
await Task.Delay(60 * 1000).ConfigureAwait(false);
// Find out how long Task.Delay delayed for.
TimeSpan duration = DateTime.Now - startTime; // THIS SOMETIMES DENOTES TIMES MUCH LARGER THAN EXPECTED, I.E. 80+ SECONDS VS. 60
if(!taskQueue.ContainsKey(workId)){
// Do something based on work being complete
}else{
// Work is not complete, inform outside source we're still working
QueueCheckinOnWorkTask(workId); // Note the non-awaited task
}
}
Keep in mind, this is example code just to show a extremely miniminal version of what is going on with my actual program.
My problem is that Task.Delay() is delaying for longer than the time specified. Something is blocking this from continuing in a reasonable timeframe.
Unfortunately I haven't been able to replicate the issue on my development machine and it only happens on the server every couple of days. Lastly, it seems related to the number of work tasks we have running at a time.
What would cause this to delay longer than expected? Additionally, how might one go about debugging this type of situation?
This is a follow up to my other question which did not receive an answer: await Task.Delay() delaying for longer that expected
Most often that happens because of thread pool saturation. You can clearly see its effect with this simple console application (I measure time the same way you are doing, doesn't matter in this case if we use stopwatch or not):
public class Program {
public static void Main() {
for (int j = 0; j < 10; j++)
for (int i = 1; i < 10; i++) {
TestDelay(i * 1000);
}
Console.ReadKey();
}
static async Task TestDelay(int expected) {
var startTime = DateTime.Now;
await Task.Delay(expected).ConfigureAwait(false);
var actual = (int) (DateTime.Now - startTime).TotalMilliseconds;
ThreadPool.GetAvailableThreads(out int aw, out _);
ThreadPool.GetMaxThreads(out int mw, out _);
Console.WriteLine("Thread: {3}, Total threads in pool: {4}, Expected: {0}, Actual: {1}, Diff: {2}", expected, actual, actual - expected, Thread.CurrentThread.ManagedThreadId, mw - aw);
Thread.Sleep(5000);
}
}
This program starts 100 tasks which await Task.Delay for 1-10 seconds, and then use Thread.Sleep for 5 seconds to simulate work on a thread on which continuation runs (this is thread pool thread). It will also output total number of threads in thread pool, so you will see how it increases over time.
If you run it you will see that in almost all cases (except first 8) - actual time after delay is much longer than expected, in some cases 5 times longer (you delayed for 3 seconds but 15 seconds has passed).
That's not because Task.Delay is so imprecise. The reason is continuation after await should be executed on a thread pool thread. Thread pool will not always give you a thread when you request. It can consider that instead of creating new thread - it's better to wait for one of the current busy threads to finish its work. It will wait for a certain time and if no thread became free - it will still create a new thread. If you request 10 thread pool threads at once and none is free, it will wait for Xms and create new one. Now you have 9 requests in queue. Now it will again wait for Xms and create another one. Now you have 8 in queue, and so on. This wait for a thread pool thread to become free is what causes increased delay in this console application (and most likely in your real program) - we keep thread pool threads busy with long Thread.Sleep, and thread pool is saturated.
Some parameters of heuristics used by thread pool are available for you to control. Most influential one is "minumum" number of threads in a pool. Thread pool is expected to always create new thread without delay until total number of threads in a pool reaches configurable "minimum". After that, if you request a thread, it might either still create new one or wait for existing to become free.
So the most straightforward way to remove this delay is to increase minimum number of threads in a pool. For example if you do this:
ThreadPool.GetMinThreads(out int wt, out int ct);
ThreadPool.SetMinThreads(100, ct); // increase min worker threads to 100
All tasks in the example above will complete at the expected time with no additional delay.
This is usually not recommended way to solve this problem though. It's better to avoid performing long running heavy operations on thread pool threads, because thread pool is a global resource and doing this affects your whole application. For example, if we remove Thread.Sleep(5000) in the example above - all tasks will delay for expected amount of time, because all what keeps thread pool thread busy now is Console.WriteLine statement which completes in no time, making this thread available for other work.
So to sum up: identify places where you perform heavy work on thread pool threads and avoid doing that (perform heavy work on separate, non-thread-pool threads instead). Alternatively, you might consider increasing minimum number of threads in a pool to a reasonable amount.

System.Threading.Timer ... Time of next fire?

I have a question about the timer. Does interval time count time of callback executions? I mean for example - I have a timer set to fire every 15 seconds and it executes a callback function that lasts approximately 3 seconds in time. When the next time the timer will fire? In 18 seconds (after callback completes) or after 15 seconds (without waiting for callback)???
Thanks in advance
It will fire every 15 seconds regardless of the callback execution time. If you want it to include the callback execution time, you can suspend and restart the timer in the callback as follows;
At the start:
someTimer.Change(Timeout.Infinite, Timeout.Infinite)
.. and at the end, change it back with the same method:
someTimer.Change(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15))
It will fire every 15 secs. Any delays by the callback do not affect the timer. The callback executes on a separate ThreadPool thread. From the docs
The method does not execute on the thread that created the timer; it executes on a ThreadPool thread supplied by the system.
If you want to take the processing time into account, you would have to manage the timer yourself - start a single-fire timer and reset it from inside the callback, eg:
TimerCallback tcb = MyCallBack;
//Start the timer once after 15 secs
_timer=new Timer(tcb,null,TimeSpan.FromSeconds(15),TimeSpan.Infinite);
...
void MyCallBack(Object stateInfo)
{
....
//Reset the timer
_timer.Change(TimeSpan.FromSeconds(15),TimeSpan.Infinite);
}
An easier and cleaner way is to use async/await to wait X seconds after each asynchronous execution. For example, the following code will execute a method 15 secs after its last execution:
while(...)
{
await Task.Delay(TimeSpan.FromSeconds(15));
var response=await Task.Run(()=>someMethod());
...
}
or, if you want to execute code that is already asynchronous
while(...)
{
await Task.Delay(TimeSpan.FromSeconds(15));
await myHttpClient.GetStringAsync(someURL);
}
Under the hood, Task.Delay creates a single-fire timer and completes when the timer finishes. It's almost the same thing you would do manually.

using await Task.Delay in a for kills performance

Let's say I want to start roughly N tasks per second distributed equally.
So I tried this:
public async Task Generate(int numberOfCallsPerSecond)
{
var delay = TimeSpan.FromMiliseconds(1000/numberOfCallsPerSecond); // a call should happen every 1000 / numberOfCallsPerSecond miliseconds
for (int i=0; i < numberOfcallsPerSecond; i++)
{
Task t = Call(); // don't wait for result here
await Task.Delay(delay);
}
}
At first I expected this to run in 1 second but for numberOfCallsPerSecond = 100 it takes 16 seconds on my 12 core CPU.
It seems the await Task.Delay adds a lot of overhead (of course without it in place generation of the calls happens in 3ms.
I didn't expect that await would add so much overhead in this scenario. Is this normal?
EDIT:
Please forget about the Call(). Running this code shows similiar result:
public async Task Generate(int numberOfCallsPerSecond)
{
var delay = TimeSpan.FromMiliseconds(1000/numberOfCallsPerSecond); // a call should happen every 1000 / numberOfCallsPerSecond miliseconds
for (int i=0; i < numberOfcallsPerSecond; i++)
{
await Task.Delay(delay);
}
}
I tried to run it with numberOfCallsPerSecond = 500 and it takes around 10 seconds, I expected Generate to take roughly 1 second, not 10 times more
Task.Delay is lightweight but not accurate. Since the loop without delay completes much faster, it sounds like your thread is going idle and using an OS sleep to wait for the timer to elapse. The timer is checked according to the OS thread scheduling quantum (in the same interrupt handler which performs thread pre-emption), which is 16ms by default.
You can reduce the quantum with timeBeginPeriod, but a better (more power efficient) approach if you need rate limiting rather than exact timing is to keep track of elapsed time (the Stopwatch class is good for this) and number of calls made, and only delay when calls made have caught up to elapsed time. The overall effect is that your thread will wake up ~60 times per second, and start a few work items each time it does. If your CPU becomes busy with something else, you'll start extra work items when you get control back -- although it's also pretty straightforward to cap the number of items started at once, if that's what you need.
public async Task Generate(int numberOfCallsPerSecond)
{
var elapsed = Stopwatch.StartNew();
var delay = TimeSpan.FromMilliseconds(1000/numberOfCallsPerSecond); // a call should happen every 1000 / numberOfCallsPerSecond milliseconds
for (int i=0; i < numberOfcallsPerSecond; i++)
{
Call(); // don't wait for result here
int expectedI = elapsed.Elapsed.TotalSeconds * numberOfCallsPerSecond;
if (i > expectedI) await Task.Delay(delay);
}
}
My psychic debugger says your Call method has a significant synchronous part (i.e the part before an await) which takes time to execute synchronously.
If you want the Generate method only to "fire up" these Call calls and have them run concurrently (including the synchronous parts) you need to offload them to a ThreadPool thread using Task.Run:
var task = Task.Run(() => Call());
await Task.Delay(delay);
Task.Delay adds almost no overhead. It uses a System.Threading.Timer internally that requires very little resources.
If you use a timespan with Task.Delay(), it'll kill the CPU. Use an integer and it wont. True story. no idea why.

execute x number of Actions each every n milliseconds

I have a program where I let the user create several functions and once he creates all the functions I run them every x milliseconds. In other words I have something like:
// functionsToExecute is of type = List<Action>
// x = some integer
while(true){
foreach(Action action in functionsToExecute)
{
action();
}
Thread.Sleep(x);
}
Now I will like for the user to decide how long to wait per function. For example if the user creates 2 functions he might want the first function to run every 500 milliseconds the next one every 1500. I was thinking about creating two threads for this scenario and then have the same implementation. But what if the user creates 50 functions? I will need 50 threads!
In short I will like to execute x number of Actions each every n milliseconds... What will be the best way to create such algorithm? For example if I have 3 Actions I will like to execute the first action every 200 milliseconds, the next one every 500 milliseconds and the last one every 1000 milliseconds.
Maybe I need something similar to the SetTimout function in javascript
If you're using .NET 4.5 and your code is not time-critical, then you can easily do this with the Task Parallel Library:
static Task Repeat (List<Action> actions, CancellationToken token, int delay)
{
var tasks = new List<Task> ();
var cts = CancellationTokenSource.CreateLinkedTokenSource (token);
foreach (var action in actions) {
var task = Task.Factory.StartNew (async () => {
while (true) {
cts.Token.ThrowIfCancellationRequested ();
await Task.Delay (delay, cts.Token).ConfigureAwait (false);
action ();
}
});
tasks.Add (task);
}
return Task.WhenAll (tasks);
}
Ideally, you should also make your actions async to properly support cancellation.
The .NET runtime automatically takes care of thread scheduling, but there's no guarantee that your action will be executed after exactly the requested timeout. It will be executed after at least that time has elapsed and there's an idle thread available.
i would consider using a ThreadPool (walkthrough). Create each thread to process and have it repeat based on the timeout they're looking for. You can also store the ManualResetEvent for when you need the thread(s) to stop.

Categories

Resources