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

本文关键字:成功 超时 重试 何异步 异步 | 更新日期: 2023-09-27 18:28:30

我写下了以下几行代码:

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属性调用将阻塞调用程序。因此,我完全不明白如何完成这项任务:"如何异步执行任务,因此如果超时,它将返回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();
    }
}