Я ищу эффективный способ выбросить исключение тайм-аута, если синхронный метод слишком долго выполняется. Я видел несколько примеров, но ничего не делает, что я хочу.
Что мне нужно сделать, это
- Убедитесь, что метод синхронизации превышает 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;
  }
