Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to delay a C# Action like QTimer::singleShot?

Tags:

c#

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...*/ );
like image 513
Damien Avatar asked Nov 15 '18 11:11

Damien


People also ask

Is there a delay function in C?

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).

How do you use delay function?

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.

What is delay 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.

How to use delay () function in C++?

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++

What does ‘void delay’ mean 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.

How can I delay execution for smaller slices of time?

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.

What is the delay in C programming for printf?

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.


3 Answers

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.

like image 172
Kzrystof Avatar answered Oct 20 '22 05:10

Kzrystof


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();

like image 42
Panagiotis Kanavos Avatar answered Oct 20 '22 04:10

Panagiotis Kanavos


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();.

like image 45
Enigmativity Avatar answered Oct 20 '22 04:10

Enigmativity