Here is a fully worked example based on the top voted answer, which is:
int timeout = 1000;
var task = SomeOperationAsync();
if (await Task.WhenAny(task, Task.Delay(timeout)) == task) {
// task completed within timeout
} else {
// timeout logic
}
The main advantage of the implementation in this answer is that generics have been added, so the function (or task) can return a value. This means that any existing function can be wrapped in a timeout function, e.g.:
Before:
int x = MyFunc();
After:
// Throws a TimeoutException if MyFunc takes more than 1 second
int x = TimeoutAfter(MyFunc, TimeSpan.FromSeconds(1));
This code requires .NET 4.5.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TaskTimeout
{
public static class Program
{
/// <summary>
/// Demo of how to wrap any function in a timeout.
/// </summary>
private static void Main(string[] args)
{
// Version without timeout.
int a = MyFunc();
Console.Write("Result: {0}\n", a);
// Version with timeout.
int b = TimeoutAfter(() => { return MyFunc(); },TimeSpan.FromSeconds(1));
Console.Write("Result: {0}\n", b);
// Version with timeout (short version that uses method groups).
int c = TimeoutAfter(MyFunc, TimeSpan.FromSeconds(1));
Console.Write("Result: {0}\n", c);
// Version that lets you see what happens when a timeout occurs.
try
{
int d = TimeoutAfter(
() =>
{
Thread.Sleep(TimeSpan.FromSeconds(123));
return 42;
},
TimeSpan.FromSeconds(1));
Console.Write("Result: {0}\n", d);
}
catch (TimeoutException e)
{
Console.Write("Exception: {0}\n", e.Message);
}
// Version that works on tasks.
var task = Task.Run(() =>
{
Thread.Sleep(TimeSpan.FromSeconds(1));
return 42;
});
// To use async/await, add "await" and remove "GetAwaiter().GetResult()".
var result = task.TimeoutAfterAsync(TimeSpan.FromSeconds(2)).
GetAwaiter().GetResult();
Console.Write("Result: {0}\n", result);
Console.Write("[any key to exit]");
Console.ReadKey();
}
public static int MyFunc()
{
return 42;
}
public static TResult TimeoutAfter<TResult>(
this Func<TResult> func, TimeSpan timeout)
{
var task = Task.Run(func);
return TimeoutAfterAsync(task, timeout).GetAwaiter().GetResult();
}
private static async Task<TResult> TimeoutAfterAsync<TResult>(
this Task<TResult> task, TimeSpan timeout)
{
var result = await Task.WhenAny(task, Task.Delay(timeout));
if (result == task)
{
// Task completed within timeout.
return task.GetAwaiter().GetResult();
}
else
{
// Task timed out.
throw new TimeoutException();
}
}
}
}
Caveats
Having given this answer, its generally not a good practice to have exceptions thrown in your code during normal operation, unless you absolutely have to:
- Each time an exception is thrown, its an extremely heavyweight operation,
- Exceptions can slow your code down by a factor of 100 or more if the exceptions are in a tight loop.
Only use this code if you absolutely cannot alter the function you are calling so it times out after a specific TimeSpan
.
This answer is really only applicable when dealing with 3rd party library libraries that you simply cannot refactor to include a timeout parameter.
How to write robust code
If you want to write robust code, the general rule is this:
Every single operation that could potentially block indefinitely, must have a timeout.
If you do not observe this rule, your code will eventually hit an operation that fails for some reason, then it will block indefinitely, and your app has just permanently hung.
If there was a reasonable timeout after some time, then your app would hang for some extreme amount of time (e.g. 30 seconds) then it would either display an error and continue on its merry way, or retry.
CancellationTokenSource
. Two overloads to the constructor are available, one taking a integer millisecond delay and one taking a TimeSpan delay. – Arvell