Qt has a neat functionality to do timed action with Lambda.
An action can be done after a delay with a single line of code:
QTimer::singleShot(10, [=](){
// do some stuff
});
Although I haven't found equivalent in C#.
The closest I got was
Timer timer = new Timer();
timer.Interval = 10;
timer.Elapsed += (tsender, args) => {
// do some stuff
timer.Stop();
};
timer.Start();
But it's far from (visually) clean.
Is there a better way to achieve this ?
The use case is sending data on a serial line to some hardware, upon a button click or action, it is often required to send a command, and a packet a few ms later.
Solution with a helper function:
public void DelayTask(int timeMs, Action lambda)
{
System.Timers.Timer timer = new System.Timers.Timer();
timer.Interval = timeMs;
timer.Elapsed += (tsender, args) => { lambda.Invoke(); };
timer.AutoReset = false;
timer.Start();
}
Called by
DelayTask(10, () => /* doSomeStuff...*/ );
Delay in C: delay function is used to suspend execution of a program for a particular time. Declaration: void delay(unsigned int); Here unsigned int is the number of milliseconds (remember 1 second = 1000 milliseconds).
The way the delay() function works is pretty simple. It accepts a single integer (or number) argument. This number represents the time (measured in milliseconds). The program should wait until moving on to the next line of code when it encounters this function.
Description. The Delay function models the Laplace expression e-sT, where T is the length of the delay in seconds and s is the Laplace operator. This is quite a simple function, which feeds the input into one end of an array table and as time advances, progresses to the end of the array.
There can be many instances when we need to create a delay in our programs. C++ provides us with an easy way to do so. We can use a delay() function for this purpose in our code. This function is imported from the “dos.h” header file in C++. We can run the code after a specific time in C++ using delay() function. delay() function in C++
It is as follows: Here, void suggests that this function returns nothing. ‘delay’ is the function name. The function takes one parameter which is unsigned integer.
So a desire exists for a function that delays execution for smaller slices of time. For that duty you’ll need to code your own function, similar to the delay () function I use. The delay () function is built upon a C library function called clock (). The clock () function returns a time value in clock ticks, which is based on the processor’s speed.
C programming code for delay. This C program exits in ten seconds, after the printf function is executed the program waits for 10000 milliseconds or 10 seconds and then it terminates.
The closest thing I would think of would be something like an helper function like you suggested:
public static class DelayedAction
{
public static Task RunAsync(TimeSpan delay, Action action)
{
return Task.Delay(delay).ContinueWith(t => action(), TaskScheduler.FromCurrentSynchronizationContext());
}
}
The usage of the class would be close to what you know with Qt:
await DelayedAction.RunAsync(TimeSpan.FromSeconds(10), () => /* do stuff */);
Update
As mentioned in an existing SO question, ContinueWith
does not keep the synchronization context by default.
In the current question, the lambda is updating some UI control and, as such, must be run on the UI thread.
To do so, the scheduler must specify the synchronization context when calling the method ContinueWith
(TaskScheduler.FromCurrentSynchronizationContext()
) to make sure such update is possible.
You should use System.Threading.Timer instead of System.Timers.Timer. System.Timers.Timer
is multithreaded timer meant to be used with desktop applications, which is why it inherits from Component and requires configuration through properties.
With a System.Threading.Timer
though you can create a single-fire timer with a single constructor call :
var timer= new Timer(_=>lambda(),null,timeMS,Timeout.Infinite);
This quick & dirty console app:
static void Main(string[] args)
{
var timeMS = 1000;
var timer = new Timer(_ => Console.WriteLine("Peekaboo"), null, timeMS, Timeout.Infinite);
Console.ReadKey();
}
Will print Peekaboo
after 1 second even though the main thread is blocked by ReadKey();
Using Microsoft's Reactive Framework (NuGet "System.Reactive") you can do this:
IDisposable subscription =
Observable
.Timer(TimeSpan.FromMilliseconds(10.0))
.Subscribe(_ => { /* Do Stuff Here */ });
The IDisposable
let's you cancel the subscription before it fires by calling subscription.Dispose();
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With