Task body doesn't execute - c#

Now this one is driving me crazy. Using .NET Fx 4.0, I have the following delay mechanism:
private static Task Delay(double milliseconds)
{
var tcs = new TaskCompletionSource<bool>();
System.Timers.Timer timer = new System.Timers.Timer();
timer.Elapsed += (obj, args) => { tcs.TrySetResult(true); };
timer.Interval = milliseconds;
timer.AutoReset = false;
timer.Start();
return tcs.Task;
}
I call it in the following code:
// delay the execution of SendKey to let the dialog show up
var sendKeyTask = Delay(500).ContinueWith((_) =>
{
// this gets executed when the dialog is visible
SendKeys.Send(filePath);
}, TaskScheduler.FromCurrentSynchronizationContext());
MyButton.InvokeMember("click");
sendKeyTask.Wait(3000); //will time out after 3 seconds.
....
The problem is that SendKeys.Send(filePath); line is never executed. What am I missing?

What #PatrykĆwiek said.
Remove the Wait(3000), and if you want something to happen after sendKeyTask is completed - do it in a continuation (or do it in the sendKeyTask, as it is running on the current thread.. whatever is more appropriate).

Related

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

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

Timer not working when Task is passed as parameter

I'm trying to improve some code and want a more generic implementation of creating timers that run async. In the situation below MethodA is working as expected and writes to console every 4 seconds. I'd expect MethodB to work too, but somehow it only executes one time.
public async Task InitTimers()
{
MethodA(TimeSpan.FromSeconds(4));
MethodB(TimeSpan.FromSeconds(4), ExecTimer());
}
private async Task MethodA(TimeSpan refreshTime)
{
var aTimer = new Timer();
aTimer.Elapsed += (s, e) => ExecTimer().ConfigureAwait(false);
aTimer.Interval = refreshTime.TotalMilliseconds;
aTimer.Enabled = true;
// Immediately trigger first time
ExecTimer().ConfigureAwait(false);
}
private async Task MethodB(TimeSpan refreshTime, Task task)
{
var aTimer = new Timer();
aTimer.Elapsed += (s, e) => task.ConfigureAwait(false);
aTimer.Interval = refreshTime.TotalMilliseconds;
aTimer.Enabled = true;
// Immediately trigger first time
task.ConfigureAwait(false);
}
private async Task ExecTimer()
{
Console.WriteLine("Hello World!");
}
Any idea what causes MethodB to only run once?
Your Timers are getting disposed when they fall out of scope once the methods MethodA/MethodB complete. You'd need to create them outside the Tasks or otherwise keep the Task running and the Timer in scope

Execute an action after a specific time, but if it is called manually, reset the timer

I am using System.Timers.Timer to execute an action every 10 secs. This action can also be called by other methods if some special condition arises or through UI. If the action is not called from the timer, I just reset the timer.
The code I am using...
timer = new Timer();
timer.Elapsed += (sender, args) => ExecuteAction();
timer.Interval = 10000;
timer.Enabled = true;
public void ExecuteActionAndResetTimer()
{
ExecuteAction();
timer.Stop();
timer.Start();
}
private void ExecuteAction()
{
// do the work...
}
The expected result, if 'X' is action called from timer (i.e. ExecuteAction), 'X' is action called from outside timer (i.e. ExecuteActionAndResetTimer) and 'o' is a second:
XooooXoXoXooooXoXooooX
This is working fine. I just want to know that can we do this using reactive extensions?
Thanks.
Yes, this is quite easily done with Rx.
Here's how:
var subject = new Subject<char>();
var query =
subject
.StartWith('X')
.Select(c =>
Observable
.Interval(TimeSpan.FromSeconds(10.0))
.Select(n => 'X')
.StartWith(c))
.Switch();
query.Subscribe(x => Console.Write(x));
Thread.Sleep(5000);
subject.OnNext('Q');
Thread.Sleep(15000);
subject.OnNext('W');
That produces the sequence XQXWXXXX with the final Xs going ad-infinitum.

Timer inaccurate

I am trying to implement a delay of 10 seconds before calling a a method. However, the method is being called in just one second.
private void closeDoors(Floor floor)
{
Timer timer = new Timer();
timer.Interval = 10000;
timer.Tick += delegate
{
DoorManager(floor, Operation.CLOSE, null);
};
timer.Start();
}
Where am I going wrong?
Thank you for your assistant.
Your problem may occur because of not stopping the Timer after finishing its job. The following modified code should work (as long as I've experienced with Timer):
private void closeDoors(Floor floor) {
Timer timer = new Timer();
timer.Interval = 10000;
timer.Tick += (s,e) => {
DoorManager(floor, Operation.CLOSE, null);
((Timer)s).Stop();
};
timer.Start();
}
I manage to fix it by incrementing the timer Interval as the time delay was being utilised by a process invoked by another method call insideDoorManager().

Using Timer to delay an operation a few seconds

I am trying to delay my method by using a timer:
private System.Timers.Timer _delayTimer;
private void delay()
{
_delayTimer = new System.Timers.Timer();
_delayTimer.Interval = 5000;
//_delayTimer.Enabled = true;
_delayTimer.Elapsed += _delayTimer_Elapsed;
_delayTimer.Start();
someMethod();
}
}
private void _delayTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
// delay for 5 seconds
}
When i am get into delay() method i want to start the timer, than i want the 5 seconds delay and only after that i want to execute someMethod() and currently this not happen, after execute delay() the someMethod() executed without 5 seconds delay
Your current code sets up the timer and then immediately executes someMethod. Instead of this, you need to put the actual method call inside your Elapsed handler:
private void delay()
{
_delayTimer = new System.Timers.Timer();
_delayTimer.Interval = 5000;
//_delayTimer.Enabled = true;
_delayTimer.Elapsed += _delayTimer_Elapsed;
_delayTimer.Start();
}
}
private void _delayTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
someMethod();
}
And if there's nothing else you intend to do you can simply write this inline:
_delayTimer = new System.Timers.Timer();
_delayTimer.Interval = 5000;
_delayTimer.Elapsed += (o, e) => someMethod();
_delayTimer.Start();
If you're in .Net4.5(or using BCL.Async pack) you can use Task.Delay
private async void delay()
{
await Task.Delay(5000);
someMethod();
}
If you're under .Net4.5
Try the below code. I'll suggest you to use System.Threading.Timer
var timer = new System.Threading.Timer(x => someMethod(), null, 5000, System.Threading.Timeout.Infinite);\
Don't forget when you use Threading.Timer someMethod will be invoked in ThreadPool thread, If you're accessing UI you need to marshal the call to UI thread.
If you want the current thread to pause for five seconds, then call Thread.Sleep. For example:
Thread.Sleep(TimeSpan.FromSeconds(5));
DoSomething();
Use a timer if you want something to happen five seconds from now, while you're doing something else. When the timer elapses, the action will be executed on a thread pool thread.
Also, if you only want the timer to execute one time (rather than once every five seconds), be sure to set AutoReset to false.
You need to call someMethod in the timer's Elapsed handler:
private void delay()
{
_delayTimer = new System.Timers.Timer();
_delayTimer.Interval = 5000;
_delayTimer.AutoReset = false; //so that it only calls the method once
_delayTimer.Elapsed += (s,args) => someMethod();
_delayTimer.Start();
}
You could also use Task.Delay instead:
private void delay()
{
Task.Delay(5000)
.ContinueWith(t => someMethod());
}
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
System.Threading.Thread.Sleep(5000);
/*
* Here Yopur code to do some method :D
* */
});

Categories