Can the Elapsed callback of a System.Timers.Timer be async? - c#

Is it possible (or even reasonable) to make the callback of a System.Timers.Timer an async method? Something like:
var timer = new System.Timers.Timer
{
Interval = TimeSpan.FromSeconds(30).TotalMilliseconds,
AutoReset = true
};
timer.Elapsed += async (sender, e) => { /* await something */ };
timer.Start();
It compiles (obviously a good place to start), but I'm not sure I understand the consequences. Will the timer await the callback before resetting the timer?

Will the timer await the callback before resetting the timer?
No. There's nothing it could await, because the signature of ElapsedEventHandler has a void return type.
In other words, your code is equivalent to:
var timer = new System.Timers.Timer { ... };
timer.Elapsed += Foo;
timer.Start();
...
private async void Foo()
{
...
}
Whether that's acceptable for you or not will depend on your context. In general, having async void methods or anonymous functions makes them harder to test and reuse - but the ability was precisely given for the sake of event handlers... You should consider how errors will be propagated though.

The title of the question is specifically about Timers, but if we look at it as "How to call an async method after some time?" then you could do it without using a timer.
var task2 = Task.Run(async () => {
while (true)
{
try
{
await MyMethod2();
} catch
{
//super easy error handling
}
await Task.Delay(TimeSpan.FromSeconds(5));
}
});
...
public async Task MyMethod2()
{
//async work here
}
Please note however that this will have different timing (timer will be called at an interval, the code above will be called every (run time + sleep_time), but even if MyMethod2 takes a long time it it won't be called twice. Having said that, you can calculate how long to await for to run 'every x minutes'.

Actually, you can.
System.Timers.Timer timer = new System.Timers.Timer();
timer.Elapsed += async (x, y) => { await Task.Delay(1); };

The solution that proposed #tymtam doesn´t wait until MyMethod2 has ended.
I think it would be better to use this. An example with two async tasks, when both has finised, wait 5 seconds and execute again the two tasks:
var task2 = Task.Run(async () => {
while (true)
{
try
{
var task1 = MyMethod1();
var task2 = MyMethod2();
List<Task> allTasks = new List<Task> { task1, task2 };
while (allTasks.Count > 0)
{
Task finishedTask = await Task.WhenAny(allTasks);
if (finishedTask == task1)
{
Console.WriteLine("MyMethod1 has ended");
}
else if (finishedTask == task2)
{
Console.WriteLine("MyMethod2 has ended");
}
tareas.Remove(finishedTask);
}
//Here only when finished all task
} catch
{
//super easy error handling
}
//Wait until next cycle
await Task.Delay(TimeSpan.FromSeconds(5));
}
});
...
public async Task MyMethod1()
{
//async work here
}
public async Task MyMethod2()
{
//async work here
}

Related

C# Task wait and timeout

I am trying to use the following technique to be able to have a worker task executing some operations, with a 10 sec timeout and without blocking the application.
internal void ReadAll()
{
var data = new byte[1];
Task.Factory.StartNew(() =>
{
var ct = new CancellationTokenSource();
var ReadAllTask = Task.Factory.StartNew(() =>
{
// Read all information
// [omit communication exchange via COM port]
ct.Cancel();
}, ct.Token);
// First thread waiting 10s for the worker to finish
ReadAllTask.Wait(10000, ct.Token);
if (ReadAllTask.Status == TaskStatus.RanToCompletion)
{
ReadAllComplete?.Invoke(true);
}
else
{
ct.Cancel();
ReadAllComplete?.Invoke(false);
}
});
}
This method is called by pressing a button. It seems to me that in debug configuration works properly, but not in release configuration where the "first thread" never reach the wait and no event is thrown.
Your code could be a lot simpler than current version. Easiest way to make a non-blocking method for event is to mark it with async keyword and use the await keyword to start the asynchronous read operation from SerialPort.BaseStream property.
Also, CancellationTokenSource could be created with time, after that it get cancelled automatically, and the right way to cancel is to call CancellationToken.ThrowIfCancellationRequested method. async/await mechanism will invoke the event in UI context, so code could be something like this:
// async void is a recommended way to use asynchronous event handlers
private async void btnReadAll_Click(object sebder, EventArgs e)
{
var data = new byte[2];
// cancel source after 10 seconds
var cts = new CancellationTokenSource(10000);
// Read all information
// [omit communication exchange via COM port]
// async operation with BaseStream
var result = await SerialPort.BaseStream.ReadAsync(data, 0, 2, cts.Token);
/*
* if you can't use the BaseStream methods, simply call this method here
* cts.Token.ThrowIfCancellationRequested();
*/
// this code would run only if everything is ok
// check result here in your own way
var boolFlag = result != null;
ReadAllComplete?.Invoke(boolFlag);
}
Here's just a quick rewrite to remove the event and wrap what appears to be a synchronous IO API in an async one. If at all possible you should switch to a true async API and drop the Task.Run.
private CancellationTokenSource cts;
public async void MyButtonhandler(object sender, EventArgs e) {
cts = new CancellationTokenSource();
try {
var result = await Task.Run(() => ReadAll(cts));
if (result) {
//success
} else {
//failure
}
} catch (TaskCanceledException ex) {
}
}
internal async Task<bool> ReadAll(CancellationTokenSource cts) {
byte[] data = new byte[1];
var timeout = TimeSpan.FromSeconds(10);
var ReadAllTask = Task.Run(() => {
// Read all information
// [omit communication exchange via COM port]
}, cts.Token);
if (await Task.WhenAny(ReadAllTask, Task.Delay(timeout)) == ReadAllTask) {
return true;
}
cts.Cancel();
return false;
}
Reading comments and answers to my question I learned a couple of useful things that solve my problem:
CancellationTokenSource can have an implicit timeout
use Task.Run instead Task.Factory.StartNew
don't need to cancel the task, the cts will do the work
Now my code is simpler and it works:
private void Read_All_Button_Click(object sender, RoutedEventArgs e)
{
// Start timedout task that will send all necessary commands
CancellationTokenSource cts = new CancellationTokenSource(10000);
Task.Run(() =>
{
oCommandSets.ReadAll(cts);
}, cts.Token);
}
and
internal void ReadAll(CancellationTokenSource cts)
{
// [communication]
if (cts.IsCancellationRequested)
{
ReadAllComplete?.Invoke(false);
}
else
{
ReadAllComplete?.Invoke(true);
}
}
In any case I need to learn more about multithreading.

Can I wrap Task.Run under another Task.Run()?

I have a method HandleAcceptedConnection that is under Task.Run() that i want to run asynchronously(in another separate thread). I tried declaring HandleAcceptedConnection as async method and dont call await but it doesnt seem to run asynchronously. I can confirm that I can have Task.Run()(by watching the thread id) under another Task.Run() but is that recommended?
private async void Start_Click(object sender, RoutedEventArgs e)
{
var task = Task.Run(() =>
{
while (isContinue)
{
var handler = listener.Accept();
// handle connection
Log("Before");
Log("ThreadId Accept " + Thread.CurrentThread.ManagedThreadId);
// i want to run method below asynchronously. i want to
// wrap it under Task.Run() but i am already under
// Task.Run(). i set HandleAcceptedConnection as async. i thought by not
// calling await on HandleAcceptedConnection, HandleAcceptedConnection
// is asynchronous
HandleAcceptedConnection(handler);
Log("After");
isContinue = true;
}
});
await task;
}
private async Task HandleAcceptedConnection(Socket handler)
{
Log("ThreadId HandleAcceptedConnection " + Thread.CurrentThread.ManagedThreadId);
Log("Under HandleAcceptedConnection");
Thread.Sleep(10000);
}
When i run this, logs says
Before
Under HandleAcceptedConnection
After
i want
Before
After
Under HandleAcceptedConnection
i want HandleAcceptedConnection to be run asynchronously. Should i wrap it under another Task.Run or it is already asynchronous?
Did you try
private async Task HandleAcceptedConnection(Socket handler)
{
Thread.Sleep(1000);
Log("Under HandleAcceptedConnection");
}
Because doing something on another thread doesn't mean it'll be delayed.
You should be using AcceptTcpClientAsync, then you won't need extra threads. Check this answer for an example. Don't use a synchronous API when there is a naturally asynchronous version of it available.
Updated to address the comment. Nothing prevents you from using Task.Run from inside Task.Run, you code might look like this (untested):
private async void Start_Click(object sender, RoutedEventArgs e)
{
var connectionTasks = new List<Task>();
Func<Task> handleConnection = async () =>
{
var connectionTask = Task.Run(() => HandleAcceptedConnection(handler));
connectionTasks.Add(connectionTask);
await connectionTask;
connectionTasks.Remove(connectionTask);
};
var task = Task.Run(() =>
{
while (isContinue)
{
var handler = listener.Accept();
// handle connection
Log("Before");
Log("ThreadId Accept " + Thread.CurrentThread.ManagedThreadId);
var connectionTask = handleConnection();
Log("After");
isContinue = true;
}
});
await task;
}

Monitoring a synchronous method for timeout

I'm looking for an efficient way to throw a timeout exception if a synchronous method takes too long to execute. I've seen some samples but nothing that quite does what I want.
What I need to do is
Check that the sync method does exceed its SLA
If it does throw a timeout exception
I do not have to terminate the sync method if it executes for too long. (Multiple failures will trip a circuit breaker and prevent cascading failure)
My solution so far is show below. Note that I do pass a CancellationToken to the sync method in the hope that it will honor a cancellation request on timeout. Also my solution returns a task that can then be awaited on etc as desired by my calling code.
My concern is that this code creates two tasks per method being monitoring. I think the TPL will manage this well, but I would like to confirm.
Does this make sense? Is there a better way to do this?
private Task TimeoutSyncMethod( Action<CancellationToken> syncAction, TimeSpan timeout )
{
var cts = new CancellationTokenSource();
var outer = Task.Run( () =>
{
try
{
//Start the synchronous method - passing it a cancellation token
var inner = Task.Run( () => syncAction( cts.Token ), cts.Token );
if( !inner.Wait( timeout ) )
{
//Try give the sync method a chance to abort grecefully
cts.Cancel();
//There was a timeout regardless of what the sync method does - so throw
throw new TimeoutException( "Timeout waiting for method after " + timeout );
}
}
finally
{
cts.Dispose();
}
}, cts.Token );
return outer;
}
Edit:
Using #Timothy's answer I'm now using this. While not significantly less code it is a lot clearer. Thanks!
private Task TimeoutSyncMethod( Action<CancellationToken> syncAction, TimeSpan timeout )
{
var cts = new CancellationTokenSource();
var inner = Task.Run( () => syncAction( cts.Token ), cts.Token );
var delay = Task.Delay( timeout, cts.Token );
var timeoutTask = Task.WhenAny( inner, delay ).ContinueWith( t =>
{
try
{
if( !inner.IsCompleted )
{
cts.Cancel();
throw new TimeoutException( "Timeout waiting for method after " + timeout );
}
}
finally
{
cts.Dispose();
}
}, cts.Token );
return timeoutTask;
}
If you have a Task called task, you can do this:
var delay = Task.Delay(TimeSpan.FromSeconds(3));
var timeoutTask = Task.WhenAny(task, delay);
If timeoutTask.Result ends up being task, then it didn't timeout. Otherwise, it's delay and it did timeout.
I don't know if this is going to behave identically to what you have implemented, but it's the built-in way to do this.
I have re-written this solution for .NET 4.0 where some methods are not available e.g.Delay. This version is monitoring a method which returns object. How to implement Delay in .NET 4.0 comes from here: How to put a task to sleep (or delay) in C# 4.0?
public class OperationWithTimeout
{
public Task<object> Execute(Func<CancellationToken, object> operation, TimeSpan timeout)
{
var cancellationToken = new CancellationTokenSource();
// Two tasks are created.
// One which starts the requested operation and second which starts Timer.
// Timer is set to AutoReset = false so it runs only once after given 'delayTime'.
// When this 'delayTime' has elapsed then TaskCompletionSource.TrySetResult() method is executed.
// This method attempts to transition the 'delayTask' into the RanToCompletion state.
Task<object> operationTask = Task<object>.Factory.StartNew(() => operation(cancellationToken.Token), cancellationToken.Token);
Task delayTask = Delay(timeout.TotalMilliseconds);
// Then WaitAny() waits for any of the provided task objects to complete execution.
Task[] tasks = new Task[]{operationTask, delayTask};
Task.WaitAny(tasks);
try
{
if (!operationTask.IsCompleted)
{
// If operation task didn't finish within given timeout call Cancel() on token and throw 'TimeoutException' exception.
// If Cancel() was called then in the operation itself the property 'IsCancellationRequested' will be equal to 'true'.
cancellationToken.Cancel();
throw new TimeoutException("Timeout waiting for method after " + timeout + ". Method was to slow :-)");
}
}
finally
{
cancellationToken.Dispose();
}
return operationTask;
}
public static Task Delay(double delayTime)
{
var completionSource = new TaskCompletionSource<bool>();
Timer timer = new Timer();
timer.Elapsed += (obj, args) => completionSource.TrySetResult(true);
timer.Interval = delayTime;
timer.AutoReset = false;
timer.Start();
return completionSource.Task;
}
}
How to use it then in Console app.
public static void Main(string[] args)
{
var operationWithTimeout = new OperationWithTimeout();
TimeSpan timeout = TimeSpan.FromMilliseconds(10000);
Func<CancellationToken, object> operation = token =>
{
Thread.Sleep(9000); // 12000
if (token.IsCancellationRequested)
{
Console.Write("Operation was cancelled.");
return null;
}
return 123456;
};
try
{
var t = operationWithTimeout.Execute(operation, timeout);
var result = t.Result;
Console.WriteLine("Operation returned '" + result + "'");
}
catch (TimeoutException tex)
{
Console.WriteLine(tex.Message);
}
Console.WriteLine("Press enter to exit");
Console.ReadLine();
}
To elabolate on Timothy Shields clean solution:
if (task == await Task.WhenAny(task, Task.Delay(TimeSpan.FromSeconds(3))))
{
return await task;
}
else
throw new TimeoutException();
This solution, I found, will also handle the case where the Task has a return value - i.e:
async Task<T>
More to be found here: MSDN: Crafting a Task.TimeoutAfter Method
Jasper's answer got me most of the way, but I specifically wanted a void function to call a non-task synchronous method with a timeout. Here's what I ended up with:
public static void RunWithTimeout(Action action, TimeSpan timeout)
{
var task = Task.Run(action);
try
{
var success = task.Wait(timeout);
if (!success)
{
throw new TimeoutException();
}
}
catch (AggregateException ex)
{
throw ex.InnerException;
}
}
Call it like:
RunWithTimeout(() => File.Copy(..), TimeSpan.FromSeconds(3));

How to cancel async Task after a period of time

In my Windows Store app I have a method
public async static Task InitAds()
{
Debug.WriteLine("API: Loading Ad images");
await Task.WhenAll(ads.Select(l => l.Value).Where(l=>l!=null).Select(l => l.StartRotation()));
}
I use to download and initialize (download, parse| Ads in a project. This method is awaited when called
...
await AdReader.InitAds()
...
The problem is that Ads server sometimes responds very slowly. I want to have a timeout, say 10 seconds for this method to run. If it does not finish in this timeout, I want it to be killed and my code to continue.
What is the best way to implement this? I found How to cancel a Task in await? but it uses a TaskFactory and when I try that approach and call my method in Task.Run it is not awaited and the code continues.
Edit:
The StartRotation is also an async method calling another async methods
public async Task StartRotation(CancellationToken ct)
{
if (Images.Count == 1)
{
await Image.LoadAndSaveImage(ct);
}
if (Images.Count <2) return;
foreach (var img in Images)
{
await img.LoadAndSaveImage(ct);
}
Delay = Image.Delay;
DispatcherTimer dt = new DispatcherTimer();
dt.Interval = TimeSpan.FromMilliseconds(Delay);
dt.Tick += (s, e) =>
{
++index;
if (index > Images.Count - 1)
{
index = 0;
}
Image = Images[index];
};
dt.Start();
}
Cancellation is cooperative. You just need to pass CancellationToken into your StartRotation:
public async static Task InitAds(CancellationToken token)
{
Debug.WriteLine("API: Loading Ad images");
await Task.WhenAll(ads.Select(l => l.Value).Where(l=>l!=null).Select(l => l.StartRotation(token)));
}
And then call it as such:
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
await InitAds(cts.Token);

Best way to call a single operation at some time in the future?

I want to fire off a timer to execute once at some point in the future. I want to use a lambda expression for code brevity. So I want to do something like...
(new System.Threading.Timer(() => { DoSomething(); },
null, // no state required
TimeSpan.FromSeconds(x), // Do it in x seconds
TimeSpan.FromMilliseconds(-1)); // don't repeat
I think it's pretty tidy. But in this case, the Timer object is not disposed. What is the best way to fix this? Or, should I be doing a totally different approach here?
That approach is flawed.
You are creating an object in memory with no reference to it. This means that the timer object is available to be garbage collected. While this code will work some of the time, you cannot predict when a garbage collection will kick in and remove the timer.
For example in the code below I force a garbage collection and it causes the timer to never fire.
static void Main(string[] args)
{
DoThing();
GC.Collect();
Thread.Sleep(5000);
}
static void DoThing()
{
new System.Threading.Timer(x => { Console.WriteLine("Here"); },
null,
TimeSpan.FromSeconds(1),
TimeSpan.FromMilliseconds(-1));
}
This will accomplish what you want, but I am not sure its the best solution. I think its something that short and elegant, but might be more confusing and difficult to follow than its worth.
System.Threading.Timer timer = null;
timer = new System.Threading.Timer(
(object state) => { DoSomething(); timer.Dispose(); }
, null // no state required
,TimeSpan.FromSeconds(x) // Do it in x seconds
,TimeSpan.FromMilliseconds(-1)); // don't repeat
Instead of using a timer, leverage the thread pool instead:
bool fired = false;
ThreadPool.RegisterWaitForSingleObject(new ManualResetEvent(false),
(state, triggered) =>
{
fired = true;
},
0, 9000, true);
GC.Collect();
Thread.Sleep(10000);
Assert.IsTrue(fired);
This survives garbage collection since you don't have to retain a reference to anything.
You could just wrap the timer class...
class Program
{
static void Main(string[] args)
{
MyTimer.Create(
() => { Console.WriteLine("hello"); },
5000);
GC.Collect();
GC.WaitForPendingFinalizers();
Console.Read();
}
}
public class MyTimer
{
private MyTimer() { }
private Timer _timer;
private ManualResetEvent _mre;
public static void Create(Action action, int dueTime)
{
var timer = new MyTimer();
timer._mre = new ManualResetEvent(false);
timer._timer = new Timer(
(x) =>
{
action();
timer._mre.Set();
},
null,
dueTime,
Timeout.Infinite
);
new Thread(new ThreadStart(() =>
{
timer._mre.WaitOne();
timer._timer.Dispose();
})).Start();
}
}
The timer object probably implements a destructor.
You can easily verify this in documentation or in the reflector.
If this is true, you shouldn't worry about it. Unless this piece of code gets called many times, in which case you should strive for deterministic deallocation of timers, meaning you would hold an array of timers, for example.
If you have a Dispatcher and want to be in the UI (Dispatcher) thread, use this:
void MyNonAsyncFunction()
{
Dispatcher.InvokeAsync(async () =>
{
await Task.Delay(1000);
MessageBox.Show("Thank you for waiting");
});
}
This function is not async because you did not want to wait within your function. This approach might be useful if you wanted to schedule more than one events at different times, but perhaps you really want the approach below:
async void MyAsyncFunction()
{
// Do my other things
await Task.Delay(1000);
MessageBox.Show("Thank you for waiting");
}
Which does the same thing, but requires the await to happen at the end of your function.
Since you may not have a Dispatcher or want to use it, but still want to schedule multiple operations at different times, I would use a thread:
static void MyFunction()
{
// Do other things...
Schedule(1000, delegate
{
System.Diagnostics.Debug.WriteLine("Thanks for waiting");
});
}
static void Schedule(int delayMs, Action action)
{
#if DONT_USE_THREADPOOL
// If use of threadpool is undesired:
new System.Threading.Thread(async () =>
{
await Task.Delay(delayMs);
action();
}
).Start(); // No need to store the thread object, just fire and forget
#else
// Using the threadpool:
Task.Run(async delegate
{
await Task.Delay(delayMs);
action();
});
#endif
}
If you want to avoid async, I would recommend not using the threadpool and replacing the await Task.Delay(delayMs) call with a Thread.Sleep(delayMs) call
You could use TaskCompletionSource for example:
static Task<T> ExecuteLater<T>(int delay, Func<T> func)
{
var tcs = new TaskCompletionSource<T>();
var timer = new System.Timers.Timer(delay) { AutoReset = false };
timer.Elapsed += delegate { timer.Dispose(); tcs.SetResult(func()); };
timer.Start();
return tcs.Task;
}
and call it like:
var result = await ExecuteLater<int>(5000, () => 50);
Or simply call:
var result = await Task.Delay(5000).ContinueWith<int>((t) => { return 50; });
System.Reactive.Linq.Observable.Interval(TimeSpan.FromSeconds(1))
.FirstAsync()
.Subscribe(_ => DoSomething()));

Categories

Resources