1

我正在尝试在 C#中使用Polly包。我想运行一些代码,然后,如果失败,请等待并重试。目前我的循环看起来与此类似:

var successful = false
while (!successful){
    // Try to perform operation.
    successful = TryToDoStuff()
    if (!successful){
        // Wait, then retry.
        await Task.WhenAny(
            taskCompletionSource1.Task,
            taskCompletionSource1.Task,
            Task.Delay(TimeSpan.FromSeconds(10)));
    }
}

即:等待 10 秒或直到这些任务完成源之一收到信号并终止。然后重试。

我想做的是这样的(Polly API 不支持):

Policy
    .Handle<RetryException>()
    .WaitAndRetryForever(
        Task.WhenAny(
            taskCompletionSource1.Task,
            taskCompletionSource1.Task,
            Task.Delay(TimeSpan.FromSeconds(10))))
    .Execute(TryToDoStuff); // Method TryToDoStuff will throw RetryException if it fails 

有可能用波莉做这样的事情吗?我可以等待 TimeSpan 以外的任何东西吗?

关于我在上面的例子中等待的两个任务:一个任务是取消,表明整个事情应该关闭。另一个是“唤醒连接尝试”任务,其终止指示“此对象的状态已更改;尝试再次调用它”。在这两种情况下,我都希望我的循环立即继续下一次迭代,而不是等待超时过去。

目前等待超时并不是那么糟糕,因为它只有 10 秒,但如果我将其更改为指数退避,那么突然超时可能会很长。因此,我希望中断超时并直接进行下一次迭代。

注意:我的重试循环不一定要遵循异步等待模式。如果等待部分是同步和阻塞的就可以了。我只想能够使用任务完成源取消等待。

4

2 回答 2

3

所有 Polly 策略和执行都可以响应CancellationTokens 以发出取消信号

如果我理解正确,有两个要求:

  1. 如果发出取消信号,请立即重试(不再延迟)
  2. RetryException如果发生a ,则使用延时(10 秒或指数)重试

您可以使用 Polly 策略来表达这一点:

var retryImmediatelyOnCancellation = Policy
    .Handle<OperationCanceledException>()
    .RetryForever();

var retryWithDelay = Policy
    .Handle<RetryException>()
    .WaitAndRetryForever(/* specify your desired delay or delay sequence for retries */);

然后以嵌套的方式执行这两个重试策略。

就像是:

retryImmediatelyOnCancellation.Execute(() => 
{
    CancellationTokenSource externalCancellation = ... // Get the CancellationTokenSource signalling external cancellation.
    CancellationTokenSource wakeUpConnectionAttemptCancellation = ... // Get the CancellationTokenSource signalling "wake up connection attempt".
    CancellationTokenSource combinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(externalCancellation.Token, wakeUpConnectionAttemptCancellation.Token);

    retryWithDelay.Execute(ct => TryDoStuff(), combinedCancellationSource.Token);
});

如果发出信号,策略造成的延迟retryWithDelay立即取消CancellationToken


CancellationToken是比 更自然的抵消信号TaskCompletionSource。但是,如果您需要(无论出于何种原因)坚持使用 aTaskCompletionSource作为这些事件的信号,您可以将其转换为取消 a CancellationTokenSource,如下所示:

taskCompletionSource.Task.ContinueWith(t => cancellationTokenSource.Cancel());

请注意,两者TaskCompletionSource都是CancellationToken一次性的:一旦完成或取消,它们就不能被重置(未完成或取消)。上面的代码示例CancellationTokenSource在 retry-on-cancellation 循环中移动了 s 的创建,以便CancellationTokenSource在每个取消信号之后获得新的 s。


如果您切换到异步,所有这些也适用于异步 Polly 策略。

于 2019-12-19T08:55:22.997 回答
0

我能想到的最好的解决方案是:

var successful = false
while (!successful){

    // Create cancellation token that gets cancelled when one of the tasks terminates.
    var cts = new CancellationTokenSource();
    _ = Task.Run(async () =>
    {
        await Task.WhenAny(
            taskCompletionSource1.Task,
            taskCompletionSource1.Task);
        cts.Cancel();
    });

    // Try to perform operation.
    Policy
        .Handle<RetryException>()
        .WaitAndRetryForever(
            TimeSpan.FromSeconds(10))
        .Execute(
            // Method TryToDoStuff will throw RetryException if it fails 
            ct => TryToDoStuff(), 
            // Pass in cancellation token.
            cts.Token);
}

这似乎有效。但如果没有波莉,我最终可能会这样做。

于 2019-12-19T08:02:42.623 回答