如何重试直到成功或超时异步?

问题描述:

我写下了几行代码:如何重试直到成功或超时异步?

public static bool RetryUntilSuccessOrTimeoutAsync(Func<bool> task, 
TimeSpan executionTimeout, CancellationToken? token = null) { 
     var data = new ExecutionContextData(task, executionTimeout, token); 

     var nonBlockingTask = new Task<bool>(SyncTaskExecutor, data); 
     nonBlockingTask.Start(); 

     var result = nonBlockingTask.ContinueWith(t => t.Result); 
     return result.Result; 
    } 

class ExecutionContextData { 
     private readonly Func<bool> task; 
     private readonly TimeSpan executionTimeout; 
     private readonly CancellationToken? cancellationToken; 

     public ExecutionContextData(Func<bool> task, TimeSpan executionTimeout, CancellationToken? cancellationToken) { 
      this.cancellationToken = cancellationToken; 
      this.executionTimeout = executionTimeout; 
      this.task = task; 
     } 

     public Func<bool> Task { 
      get { return task; } 
     } 

     public TimeSpan ExecutionTimeout { 
      get { return executionTimeout; } 
     } 

     public CancellationToken? CancellationToken { 
      get { return cancellationToken; } 
     } 
    } 

private static bool SyncTaskExecutor(object executionHelper) { 
     var context = executionHelper as ExecutionContextData; 
     Task<bool> newTask = 
context.CancellationToken.HasValue ? new Task<bool>(ExecuteTask, context.Task, context.CancellationToken.Value) 
: new Task<bool>(ExecuteTask, context.Task); 
     newTask.Start(); 

     bool timeoutResult = newTask.Wait(context.ExecutionTimeout); 
     if (timeoutResult) 
      return newTask.Result; 
     return false; 
    } 

但据我了解Result属性invokation将阻止呼叫者。所以,我完全不知道如何完成这个任务: “如何异步执行一个任务,所以如果超时超时那么它将返回false或者它将返回应该一遍又一遍地执行的任务的结果?”

为什么不尝试这样的东西,如果你有,你可能要取消或已超时操作:

public static class Retries 
{ 
    public enum Result 
    { 
     Success, 
     Timeout, 
     Canceled, 
    } 

    public static Task<Result> RetryUntilTimedOutOrCanceled(this Func<bool> func, CancellationToken cancel, TimeSpan timeOut) 
    { 
     return Task.Factory.StartNew(() => 
     { 
      var start = DateTime.UtcNow; 
      var end = start + timeOut; 
      while (true) 
      { 
       var now = DateTime.UtcNow; 
       if (end < now) 
        return Result.Timeout; 
       var curTimeOut = end - now; 
       Task<bool> curTask = null; 
       try 
       { 
        if (cancel.IsCancellationRequested) 
         return Result.Canceled; 
        curTask = Task.Factory.StartNew(func, cancel); 
        curTask.Wait((int)curTimeOut.TotalMilliseconds, cancel); 
        if (curTask.IsCanceled) 
         return Result.Canceled; 
        if (curTask.Result == true) 
         return Result.Success; 
       } 
       catch (TimeoutException) 
       { 
        return Result.Timeout; 
       } 
       catch (TaskCanceledException) 
       { 
        return Result.Canceled; 
       } 
       catch (OperationCanceledException) 
       { 
        return Result.Canceled; 
       } 
      } 
     }); 
    } 
} 
class Program 
{ 

    static void Main(string[] args) 
    { 
     var cancelSource = new CancellationTokenSource(); 
     Func<bool> AllwaysFalse =() => false; 
     Func<bool> AllwaysTrue =() => true; 

     var result = AllwaysFalse.RetryUntilTimedOutOrCanceled(cancelSource.Token, TimeSpan.FromSeconds(3)).Result; 
     Console.WriteLine(result); 

     result = AllwaysTrue.RetryUntilTimedOutOrCanceled(cancelSource.Token, TimeSpan.FromSeconds(3)).Result; 
     Console.WriteLine(result); 

     var rTask = AllwaysFalse.RetryUntilTimedOutOrCanceled(cancelSource.Token, TimeSpan.FromSeconds(100)); 
     System.Threading.Thread.Sleep(1000); 
     cancelSource.Cancel(); 
     result = rTask.Result; 
     Console.WriteLine(result); 

     Console.ReadLine(); 
    } 
}