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...*/ );
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();.
Related
I'm unsure how best to cancel a task that is running a system timer.
In the code below, every 60 mins the timer will elapse and then run another method (CheckFileOverflow) that is used to check the file size of a system log txt. file
Cancellation of the timer ideally would be done by a button click or another method that calls the cancellation. The timer will effectively be allowed to run for as long as the software is running, but when the user eventually shuts down the software i'd like to be able to cancel the task in a responsible fashion i.e. not run the risk of ongoing thread pool resources lingering being used in the background.
I have spent many many hours reading up on cancellation tokens but still don't get it :(
public void SystemEventLoggerTimer()
{
SysEvntLogFileChckTimerRun = true;
Task.Run(() =>
{
System.Timers.Timer timer = new System.Timers.Timer
{ Interval = 1000 * 60 * 60 };
timer.Elapsed += new ElapsedEventHandler(CheckFileOverflow);
timer.Start();
});
}
I'd suggest that you use Microsoft's Reactive Framework (aka Rx) - just NuGet System.Reactive.
Then you do this:
IDisposable subscription =
Observable
.Interval(TimeSpan.FromHours(1.0))
.Subscribe(_ => CheckFileOverflow());
When you want to cancel the subscription just call subscription.Dispose().
Rx is ideal for abstracting away timers, events, tasks, async operations, etc.
You can change your method to something like this
public void SystemEventLoggerTimer(CancellationToken cancelToken)
{
SysEvntLogFileChckTimerRun = true;
Task.Run(async () =>
{
// Keep this task alive until it is cancelled
while (!cancelToken.IsCancellationRequested)
{
await Task.Delay(TimeSpan.FromMinutes(60));
CheckFileOverflow();
}
});
}
Then you call SystemEventLoggerTimer like this
var cancelSource = new CancellationTokenSource();
SystemEventLoggerTimer(cancelSource.Token);
you can cancel this Token when program has been disposed or simply at the end of your main function
Why not just have a timer accessible in the calling context (or globally in your class/application) - you'd have to do that with the CancellationTokenSource anyway! This doesn't look like the right use case for a Task.
Try this:
public void SystemEventLoggerTimer(System.Timers.Timer timer)
{
SysEvntLogFileChckTimerRun = true;
timer.Elapsed += new ElapsedEventHandler(CheckFileOverflow);
timer.Start();
}
Calling code:
var timer = new System.Timers.Timer() { Interval = 1000 * 60 * 60 };
SystemEventLoggerTimer(timer);
Cancellation code (in cancel button's event handler, etc):
timer.Stop();
I have posted below what appears to be a satisfactory solution which worked for me. Hopefully I'm responding to the thread in the correct manner... (a newbie to stackOverflow)
I setup a quick windows form for testing, I created 2qty buttons and 1qty textbox.
Buttons are used to Start & Stop the timer (using cancellation token)
The textbox is used to monitor the timer which will update with "Timer Running" message every 2 seconds. Hope this helps anyone else looking at a similar scenario...
enter image description here
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private CancellationTokenSource cancelSource;
// Button is used to START the timer.
private void TimerStartButton_Click(object sender, EventArgs e)
{
cancelSource = new CancellationTokenSource();
// Run the below method that will initiate timer to start running from
// the button click.
SystemEventLoggerTimer(cancelSource.Token);
}
private void SystemEventLoggerTimer(CancellationToken cancelToken)
{
Task.Run(async () =>
{
// Keep this task alive until it is cancelled
while (!cancelToken.IsCancellationRequested)
{
// Encapsulating the function Task.Delay with 'cancelToken'
// allows us to stop the Task.Delay during mid cycle.
// For testing purposes, have reduced the time interval to 2 secs.
await Task.Delay(TimeSpan.FromSeconds(2), cancelToken);
// Run the below method every 2 seconds.
CheckFileOverflow();
}
});
}
// When the below method runs every 2 secs, the UpdateUI will allow
// us to modify the textbox form controls from another thread.
private void CheckFileOverflow()
{
UpdateTextbox("Timer Running");
}
// UpdateUI will allow us to modify the textbox form controls from another thread.
private void UpdateTextbox(string s)
{
Func<int> del = delegate ()
{
textBox1.AppendText(s + Environment.NewLine);
return 0;
};
Invoke(del);
}
// Button that is used to STOP the timer running.
private void TimerStopButton_Click(object sender, EventArgs e)
{
// Initiate the cancelleation request to method "SystemEventLoggerTimer"
cancelSource.Cancel();
}
}
I have this Timer:
Timer delayTimer = new Timer();
delayTimer.Interval = 500;
delayTimer.Elapsed += (object sender, ElapsedEventArgs e) => {
Console.WriteLine("test");
textInputDialog.Show();
delayTimer.Stop();
};
delayTimer.Start();
Here I have the following problems:
Timer never stops. Code is executed every 500ms.
textInputDialog.Show(); doesn't work (perhaps cause of problem above)
What is wrong with my code?
Alternative solutions:
This is an alternative to timer as Jens Horstmann mentioned. And this is called on the UI thread:
private async Task SendWithDelay()
{
await Task.Delay(500);
textInputDialog.Show();
}
Another alternative would be NSTimer:
NSTimer.CreateScheduledTimer(new TimeSpan(0,0,0,0,500), delegate {
textInputDialog.Show();
});
And to invoke a call on the UI thread you can use InvokeOnMainThread:
Timer delayTimer = new Timer();
delayTimer.Interval = 500;
delayTimer.Elapsed += (object sender, ElapsedEventArgs e) => {
delayTimer.Stop();
Console.WriteLine("test");
InvokeOnMainThread (() => {
textInputDialog.Show();
});
};
delayTimer.Start();
Stop the timer before you show the dialog:
delayTimer.Elapsed += (object sender, ElapsedEventArgs e) => {
delayTimer.Stop();
Console.WriteLine("test");
textInputDialog.Show();
};
Also you probably used the wrong timer. Don't use System.Threading.Timer or System.Timers because this involves multithreading which does not work well with winforms or WPF. (This is probably the reason your MessageBox does not show - its called on the wrong thread)
In WPF you should use System.Windows.Threading.DispatcherTimer
Edit
In Winforms you should use System.Windows.Forms.Timer (see comments)
Here is a solution without async/await
It also happen to fit in a single statement, which is rather elegant.
This is a C#, cross-platform solution to perform an action after a delay.
Also works for a recurring task.
using System.Threading;
var delayTimer = new Timer((state) => // Create timer, forget about it
InvokeOnMainThread(() => // Fire on main thread
textInputDialog.Show() // Your code goes here
),
null, // Ignore the state
5 * 1000, // 5 seconds until the 1st fire
Timeout.Infinite); // Do not repeat
Something like this worked for me:
private async Task DelayedShow()
{
await Task.Delay(500);
await _loadPop.textInputDialog.Show();
}
Remember to call the method like this:
BeginInvokeOnMainThread(() => DelayedShow());
It's clear: For example, imagine a button in my form. When a user clicks on the button, some void method should run after 30 seconds.
There would be a void method DoAfterDelay that takes two input parameter. The first one is the method to do (using delegates), and the other one is the time interval. So I'll have:
public delegate void IVoidDelegate();
static void DoAfterDelay(IVoidDelegate TheMethod, TimeSpan Interval)
{
// *** Some code that will pause the process for "Interval".
TheMethod();
}
So, I just need a piece of code to pause the process for a specific time interval. Heretofore, I used this code to do that:
System.Threading.Thread.Sleep(Interval);
But this code is no good for me, because it stops the whole process and freezes the program. I don't want the program to get stuck in the DoAfterDelay method. That's why the Thread.Sleep is useless.
So could anyone suggest a better way? Of course I've searched about that, but most of the solutions I've found were based on using a timer (like here for example). But using a timer is my last opinion, because the method should run once and using timers makes the program confusing to read. So I'm looking for a better solution if there is. Or maybe I have to use timers?
I guess I have to play with threads, but not sure. So I wonder if anyone could guide me to a solution. Thanks in advance.
Can you use a task?
Task.Factory.StartNew(() =>
{
System.Threading.Thread.Sleep(Interval);
TheMethod();
});
This is where you can use the async await functionality of .Net 4.5
You can use Task.Delay an give the delay in miliseconds.
This is a very clean way. ex:
private async void button1_Click(object sender, EventArgs e)
{
await Task.Delay(5000);
TheMethod();
}
There are several methods of creating thread but of course, it depends on what you are doing.
You can create a thread on the fly like this:
Thread aNewThread = new Thread(
() => OnGoingFunction()
);
aNewThread.Start();
This thread will be running in the background. The function you want to do should have a sleep method to sleep when its done processing. So something like this:
private void OnGoingFunction()
{
//Code....
Thread.Sleep(100); //100 ms, this is in the thead so it will not stop your winForm
//More code....
}
I hope that helps.
Another option is to create the thread whenever you need to process it and not worry about the sleep option. Just create a new thread every time to load the process
You should create a Coroutine
public IEnumerator waitAndRun()
{
// WAIT FOR 3 SEC
yield return new WaitForSeconds(3);
// RUN YOUR CODE HERE ...
}
And call it with:
StartCoroutine(waitAndRun());
DoAfterDelay starts a timer that just runs once, that when it expires it calls your void 'TheMethod'function.
Why would this be messy?
You can specify the exact seconds by using
DateTime runTime = new DateTime();
double waitSeconds = (runTime - DateTime.Now).TotalSeconds;
Task.Factory.StartNew(() =>
{
Thread.Sleep(TimeSpan.FromSeconds(waitSeconds));
YourMethod();
});
runTime => When you want to execute the method.
Here's what you want:
public static void Example1c()
{
Action action = DoSomethingCool;
TimeSpan span = new TimeSpan(0, 0, 0, 5);
ThreadStart start = delegate { RunAfterTimespan(action, span); };
Thread t4 = new Thread(start);
t4.Start();
MessageBox.Show("Thread has been launched");
}
public static void RunAfterTimespan(Action action, TimeSpan span)
{
Thread.Sleep(span);
action();
}
private static void DoSomethingCool()
{
MessageBox.Show("I'm doing something cool");
}
One of the benefits of using Action is that it can be easily modified to pass in parameters. Say you want to be able to pass an integer to DoSomethingCool. Just modify thusly:
public static void Example1c()
{
Action<int> action = DoSomethingCool;
TimeSpan span = new TimeSpan(0, 0, 0, 5);
int number = 10;
ThreadStart start = delegate { RunAfterTimespan(action, span, number); };
Thread t4 = new Thread(start);
t4.Start();
MessageBox.Show("Thread has been launched");
}
public static void RunAfterTimespan(Action<int> action, TimeSpan span, int number)
{
Thread.Sleep(span);
action(number);
}
private static void DoSomethingCool(int number)
{
MessageBox.Show("I'm doing something cool");
}
Very flexible...
Here's a simple extension against Dispatcher that you can use in a non-blocking way.
public static void InvokeAfter(this Dispatcher dispatcher, int milliseconds, Action delayedAction) {
Task.Factory.StartNew(() => {
System.Threading.Thread.Sleep(milliseconds);
dispatcher.Invoke(delayedAction);
});
}
And here's how you use it with a Lambda:
SomeLabel.Dispatcher.InvokeAfter(3000, () => {
SomeLabel.Text = "Hello World";
});
You can also use it with anything that matches Action. Here's an example using a local function...
void doLater(){
SomeLabel.Text = "Hello World";
}
// Pass the action itself, not the result of the action (i.e. don't use parentheses with 'doLater'.)
SomeLabel.Dispatcher.InvokeAfter(3000, doLater);
Note: You can then call it against any dispatcher object where you would normally call Invoke. For safety, I like to invoke it using the dispatcher handling the control I'm updating.
Working on a windows service, which has to process request in every predefined interval of time. Thread.Sleep does the work perfectly fine but problem with this is when service is invoked to be stopped, service freeze if thread is in sleep mode.
I have read about the alternative approach like Timer, but problem with that is after that defined interval new thread is getting started.
Is there a better way to achieve same result and not run in to issue.
What you're looking for is the ability to respond to the notification of two different events - (1) when the timer elapses and (2) when the service is stopped. #Anurag Ranhjan is on the right track with WaitHandle, but you have two events, not one. To properly handle this, do the following.
First, define the two events you care about using ManualResetEvent. You can use AutoResetEvent if you prefer; I just prefer resetting the events manually.
using System.Threading;
ManualResetEvent shutdownEvent = new ManualResetEvent();
ManualResetEvent elapsedEvent = new ManualResetEvent();
You need to trigger these events when they occur. For the shutdownEvent, it's easy. In the OnStop callback of your Windows service, just set the event.
protected override void OnStop
{
shutdownEvent.Set();
}
For the elapsedEvent, you could do this a couple different ways. You could create a background thread, i.e., the ThreadPool, that uses Thread.Sleep. When the thread wakes up, set the elapsedEvent and go back to sleep. Since it's a background thread, it won't hang your service when it shuts down. The alternative, as you've already suggested, is to use a timer. This is how I do it.
using System.Timers;
Timer timer = new Timer();
timer.Interval = 5000; // in milliseconds
timer.Elapsed += delegate { elapsedEvent.Set(); };
timer.AutoReset = false; // again, I prefer manual control
timer.Start();
Now that you've got events being set properly, put them in a WaitHandle array.
WaitHandle[] handles = new WaitHandle[]
{
shutdownEvent,
elapsedEvent
};
Instead of the WaitHandle.WaitOne method, use the WaitHandle.WaitAny method inside a while loop, like this.
while (!shutdownEvent.WaitOne())
{
switch (WaitHandle.WaitAny(handles))
{
case 0: // The shutdownEvent was triggered!
break;
case 1: // The elapsedEvent was triggered!
Process(); // do your processing here
elapsedEvent.Reset(); // reset the event manually
timer.Start(); // restart the timer manually
break;
default:
throw new Exception("unexpected switch case");
}
}
I've condensed this example from production code in my project. I know this mechanism works, but I may have missed something in the writeup. Let me know if you have any questions.
You can use WaitHandle.WaitOne instead. You can wait for closing event to trigger or timeout that you are specifying in predefined interval of time.
static AutoResetEvent seviceStopRequested = new AutoResetEvent(false);
....
((AutoResetEvent)stateInfo).WaitOne([timeout], false)
Then when Service stop is invoked, you can just trigger the event
seviceStopRequested .Set();
I usually use the following pattern:
public class MyJob
{
System.Threading.Timer _timer;
bool _isStopped;
public void MyJob()
{
_timer = new Timer(OnWork, null, TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(-1));
}
private void OnWork(object state)
{
//[.. do the actual work here ..]
if (!_isStopped)
_timer.Change(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(-1));
}
public void Stop()
{
_isStopped = true;
_timer.Change(TimeSpan.FromSeconds(-1), TimeSpan.FromSeconds(-1));
}
public void Start()
{
_isStopped = false;
_timer.Change(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(-1));
}
}
Key points:
Only using the initial interval gives you full control of when the timer is started again (i.e. the work time is not counted in the timer interval)
Changing the timer to -1 seconds pauses it until changed again
It should therefore work with all your requirements.
Use a Timer to add commands/tasks including the task for shutdown to a blocking queue. Make your service thread to wait for tasks on the blocking queue and execute them when available. The timer thread will keep adding the tasks to the queue periodically.
For what it is worth most of the blocking calls in the .NET BCL will respond to Thread.Interrupt. That is, they will not wait for the full amount of time specified when called and instead return immediately. However, I would avoid using this method and instead use a single ManualResetEvent to perform both the idle waiting and the shutdown signal. It would look like this.
public class MyServer : ServiceBase
{
private ManualResetEvent shutdown = new ManualResetEvent(false);
protected override void OnStart(string[] args)
{
new Thread(
() =>
{
while (!shutdown.WaitOne(YourInterval))
{
// Do work here.
}
}).Start();
}
protected override void OnStop()
{
shutdown.Set();
}
}
I had a situation come up that required running a lambda expression on the UI thread after a delay. I thought of several ways to do this and finally settled on this approach
Task.Factory.StartNew(() => Thread.Sleep(1000))
.ContinueWith((t) => textBlock.Text="Done",TaskScheduler.FromCurrentSynchronizationContext());
But I'm wondering if there's an easier way that I missed. Any suggestions for a shorter, simpler or easier technique? Assume .NET 4 is available.
I think what you've got is pretty good Scott.
The only slight issue I think some might have with it, is that you're blocking a thread in order to execute your delay. Of course it's a background thread, and unlikely to cause problems unless you execute a lot of these calls concurrently (each tying up a thread), but it's still probably suboptimal.
I would instead suggest that you factor the algorithm into a utility method, and avoid using Thread.Sleep.
There's obviously probably innumerable ways of doing this, but here's one:
public static class UICallbackTimer
{
public static void DelayExecution(TimeSpan delay, Action action)
{
System.Threading.Timer timer = null;
SynchronizationContext context = SynchronizationContext.Current;
timer = new System.Threading.Timer(
(ignore) =>
{
timer.Dispose();
context.Post(ignore2 => action(), null);
}, null, delay, TimeSpan.FromMilliseconds(-1));
}
}
To use:
UICallbackTimer.DelayExecution(TimeSpan.FromSeconds(1),
() => textBlock.Text="Done");
Of course you could also write an implementation of this DelayExecution method which uses other types of timer such as the WPF DispatcherTimer or the WinForms Timer class. I'm not sure what the tradeoffs of these various timers would be. My guess would be DispatcherTimer's and WinForm's timers would actually still function on applications of the opposite type.
EDIT:
Re-reading my answer, I think actually I would be tempted to factor this into an extension method which works on synchronization contexts - if you think about it, a more general statement would be that you need to be able to post work back to a synchronization context after a certain delay.
The SynchronizationContext already has a post method for queueing work, which the original caller does not want to block on completion. What we need is a version of this that posts the work after a delay, so instead:
public static class SyncContextExtensions
{
public static void Post(this SynchronizationContext context, TimeSpan delay, Action action)
{
System.Threading.Timer timer = null;
timer = new System.Threading.Timer(
(ignore) =>
{
timer.Dispose();
context.Post(ignore2 => action(), null);
}, null, delay, TimeSpan.FromMilliseconds(-1));
}
}
and use:
SynchronizationContext.Current.Post(TimeSpan.FromSeconds(1),
() => textBlock.Text="Done");
I think the simplest way is using System.Windows.Forms.Timer, if lambda isnt some random function.
this._timer.Interval = 1000;
this._timer.Tick += (s, e) => this.textBlock.Text = "Done";
If labda has no need to be executed in the loop, add this;
this.timer1.Tick += (s, e) => this.timer1.Stop();
And call
this.timer1.Start();
where it needed.
Another way is using Invoke methodes.
delegate void FooHandler();
private void button1_Click(object sender, EventArgs e)
{
FooHandler handle = () => Thread.Sleep(1000);
handle.BeginInvoke(result => { ((FooHandler)((AsyncResult)result).AsyncDelegate).EndInvoke(result); this.textBox1.Invoke((FooHandler)(() => this.textBox1.Text = "Done")); }, null);
}
Control.Invoke guarantees that delegate would be executed in the UI thread (where parent window main descriptor exists)
Maybe exists the better variant.