다음은 최상위 투표 답변을 기반으로 한 완전한 예입니다.
int timeout = 1000;
var task = SomeOperationAsync();
if (await Task.WhenAny(task, Task.Delay(timeout)) == task) {
// task completed within timeout
} else {
// timeout logic
}
이 답변에서 구현의 주요 장점은 제네릭이 추가되어 함수 (또는 작업)가 값을 반환 할 수 있다는 것입니다. 즉, 기존 함수는 다음과 같은 시간 초과 함수로 래핑 될 수 있습니다.
전에:
int x = MyFunc();
후:
// Throws a TimeoutException if MyFunc takes more than 1 second
int x = TimeoutAfter(MyFunc, TimeSpan.FromSeconds(1));
이 코드에는 .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();
}
}
}
}
경고
이 대답을 제공하는 데, 그 일반적으로 하지 당신이 절대적으로하지 않는 한 좋은 연습은 정상 작동 중에 코드에서 발생한 예외를합니다 :
- 예외가 발생할 때마다 매우 무거운 작업,
- 예외가 엄격한 루프에있는 경우 예외로 인해 100 배 이상 코드가 느려질 수 있습니다.
호출 한 함수를 절대 변경할 수 없어 특정 코드 이후에 시간이 초과되는 경우에만이 코드를 사용하십시오 TimeSpan
.
이 답변은 실제로 타임 아웃 매개 변수를 포함하도록 리팩터링 할 수없는 타사 라이브러리 라이브러리를 처리 할 때만 적용됩니다.
강력한 코드를 작성하는 방법
강력한 코드를 작성하려면 일반적인 규칙은 다음과 같습니다.
무한정 차단 될 수있는 모든 단일 작업에는 시간 초과가 있어야합니다.
당신이 경우 하지 않는 이 규칙을 준수 코드는 결국 어떤 이유로, 다음 무기한 차단 실패하고 앱이 바로 영구적으로 중단으로 작업에 타격을 줄 것으로 예상된다.
일정 시간이 지난 후 합리적인 시간 초과가 발생한 경우 앱이 극단적 인 시간 (예 : 30 초) 동안 중단 된 경우 오류가 표시되고 계속 진행되거나 다시 시도됩니다.