In my app I am creating some concurrent web requests and I am satisfied when any one of them completes, so I am using the method Task.WhenAny:
var urls = new
Simply pass to all of your tasks the same cancellation token, something like this:
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken ct = cts.Token;
// here you specify how long you want to wait for task to finish before cancelling
int timeout = 5000;
cts.CancelAfter(timeout);
// pass ct to all your tasks and start them
await Task.WhenAny(/* your tasks here */);
// cancel all tasks
cts.Cancel();
Also, you need to read this thread to be aware of how to use CancellationToken
correctly: When I use CancelAfter(), the Task is still running
Update: better solution based on Stephen Cleary's answer and MSDN and svick's answer:
CancellationTokenSource source = new CancellationTokenSource();
source.CancelAfter(TimeSpan.FromSeconds(1));
var tasks = urls.Select(url => Task.Run( async () =>
{
using (var webClient = new WebClient())
{
token.Register(webClient.CancelAsync);
var result = (Url: url, Data: await webClient.DownloadStringTaskAsync(url));
token.ThrowIfCancellationRequested();
return result.Url;
}
}, token)).ToArray();
string url;
try
{
// (A canceled task will raise an exception when awaited).
var firstTask = await Task.WhenAny(tasks);
url = (await firstTask).Url;
}
catch (AggregateException ae) {
foreach (Exception e in ae.InnerExceptions) {
if (e is TaskCanceledException)
Console.WriteLine("Timeout: {0}",
((TaskCanceledException) e).Message);
else
Console.WriteLine("Exception: " + e.GetType().Name);
}
}
non-optimal solution
The timeout can be solved by adding a task that just waits and completes after given time. Then you check which task completed first, if it is the waiting one, then timeout effectively occurred.
Task timeout = Task.Delay(10000);
var firstTask = await Task.WhenAny(tasks.Concat(new Task[] {timeout}));
if(firstTask == timeout) { ... } //timed out
source.Cancel();