I am currently trying to implement a substitute for .Net 4.5's Task.Delay()
method in a program that must target .Net 4.0. I found the following code at this blog.
/* You can write Task-based asynchronous methods by utilizing a TaskCompletionSource.
A TaskCompletionSource gives you a 'slave' Task that you can manually signal.
Calling SetResult() signals the task as complete, and any continuations kick off. */
void Main()
{
for (int i = 0; i < 10000; i++)
{
Task task = Delay (2000);
task.ContinueWith (_ => "Done".Dump());
}
}
Task Delay (int milliseconds) // Asynchronous NON-BLOCKING method
{
var tcs = new TaskCompletionSource<object>();
new Timer (_ => tcs.SetResult (null)).Change (milliseconds, -1);
return tcs.Task;
}
Tasks
are fairly new to me. System.Threading.Timer
and TaskCompletionSource
are brand new to me (as of today), and I'm struggling a bit with them. All that aside, I'm wondering how I might add CancellationToken
functionality to this code. I'm assuming I could add a parameter to the Delay()
method like this:
Task Delay (int milliseconds, CancellationToken token) // Asynchronous NON-BLOCKING method
{
var tcs = new TaskCompletionSource<object>();
new Timer (_ => tcs.SetResult (null)).Change (milliseconds, -1);
return tcs.Task;
}
... but then, where do I put the logic for checking the token and getting out of the method? Somewhere in the callback? Is this even possible?
I've tried to change your code as little as possible but here is a working example that behaves in the same way as Task.Delay.
It's important to note that I use TrySetCanceled
and TrySetResult
because the Timer could finish after the task is canceled. Ideally you want to stop the timer.
Also note a canceled task will throw a TaskCanceledException
static void Main(string[] args)
{
// A cancellation source that will cancel itself after 1 second
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(1));
try
{
// This will only wait 1 second because as it will be cancelled.
Task t = Delay(5000, cancellationTokenSource.Token);
t.Wait();
Console.WriteLine("The task completed");
}
catch (AggregateException exception)
{
// Expecting a TaskCanceledException
foreach (Exception ex in exception.InnerExceptions)
Console.WriteLine("Exception: {0}", ex.Message);
}
Console.WriteLine("Done");
Console.ReadLine();
}
private static Task Delay(int milliseconds, CancellationToken token)
{
var tcs = new TaskCompletionSource<object>();
token.Register(() => tcs.TrySetCanceled());
Timer timer = new Timer(_ => tcs.TrySetResult(null));
timer.Change(milliseconds, -1);
return tcs.Task;
}
Reading a bit more into your question. If you need Task.Delay and you're targeting .NET 4.0 then you should use the Microsoft Async nuget package from http://www.nuget.org/packages/Microsoft.Bcl.Async/ it contains the method TaskEx.Delay
Like this:
token.Register(() => tcs.TrySetCancelled());
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