Я ищу эффективный способ выбросить исключение тайм-аута, если синхронный метод слишком долго выполняется. Я видел несколько примеров, но ничего не делает, что я хочу.
Что мне нужно сделать, это
- Убедитесь, что метод синхронизации превышает SLA
- Если он генерирует исключение тайм-аута
Я выполняю не, чтобы завершить метод синхронизации, если он выполняется слишком долго. (Несколько отказов отключат автоматический выключатель и предотвратит каскадный отказ)
Мое решение пока показано ниже. Обратите внимание, что я передаю метод CancellationToken методу синхронизации в надежде, что он выполнит запрос на отмену таймаута. Также мое решение возвращает задачу, которую можно ожидать по иному, по желанию, с помощью моего кода вызова.
Меня беспокоит, что этот код создает две задачи для каждого метода, который отслеживается. Я думаю, что TPL справится с этим хорошо, но я хотел бы подтвердить.
Это имеет смысл? Есть ли лучший способ сделать это?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:
Используя @Timothy ответ, я теперь использую это. Хотя не намного меньше кода, это намного яснее. Спасибо!
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;
}