我想实现一个重试任务,该任务执行先前的失败任务操作并重复它。
这是我到目前为止所拥有的。然而,它只是重复这样一个事实,即任务是错误的,而不是实际再次触发任务的动作。
public static async Task<T> Retry<T>(this Task<T> task, int retryCount, int delay, TaskCompletionSource<T> tcs = null)
{
if (tcs == null)
{
tcs = new TaskCompletionSource<T>();
}
await task.ContinueWith(async _original =>
{
if (_original.IsFaulted)
{
if (retryCount == 0)
{
tcs.SetException(_original.Exception.InnerExceptions);
}
else
{
Console.WriteLine("Unhandled exception. Retrying...");
await Task.Delay(delay).ContinueWith(async t =>
{
await Retry(task, retryCount - 1, delay, tcs);
});
}
}
else
tcs.SetResult(_original.Result);
});
return await tcs.Task;
}
我试图通过一点反思来获得动作。但是,似乎任务完成后,操作将设置为null。
var action = task
.GetType()
.GetField("m_action", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(task) as Action;
理想情况下,我希望我的实现看起来像这样:
try
{
await MakeFailure().Retry(5, 1000);
}
catch (Exception ex)
{
Console.WriteLine("I had an exception");
}
这可能无法实现,但我想在将代码重构为Retry(Func<T> task)
之前确定
答案 0 :(得分:2)
并非完全反对它。但它将代码流改变为我不喜欢的故障第一布局
考虑你的类型。 Task
表示异步操作。在异步世界中,Task
表示已启动的异步操作 。 Task
不是你可以“重试”的东西。
另一方面,Func<Task>
表示可以启动的异步操作。或重新启动。这就是你需要使用的东西。
使用适当的类型后,代码很简单:
public static async Task<T> Retry<T>(Func<Task<T>> action, int retryCount, int delay)
{
while (retryCount > 0)
{
try
{
return await action().ConfigureAwait(false);
}
catch (Exception ex)
{
await Task.Delay(delay).ConfigureAwait(false);
--retryCount;
}
}
return await action().ConfigureAwait(false);
}
与其他回答者一样,我建议您使用实际为此设计的库。 Transient Fault Handling Application Block和Polly是两个很好的例子。
答案 1 :(得分:1)
有一个非常棒的库,你可以在不编写自己的代码的情况下利用它。它被称为瞬态故障应用程序块。但我首先要评估名为TransientFaultHandling.Core的块中的单个库。
它的使用方式与上面的代码非常相似。这是一个简单的例子:
using System;
using Microsoft.Practices.TransientFaultHandling;
namespace Stackoverflow
{
class Program
{
internal class MyTransientErrorDetectionStrategy : ITransientErrorDetectionStrategy
{
public bool IsTransient(Exception ex)
{
return true;
}
}
private static void Main(string[] args)
{
const int retryCount = 5;
const int retryIntervalInSeconds = 1;
// define the strategy for retrying
var retryStrategy = new FixedInterval(
retryCount,
TimeSpan.FromSeconds(retryIntervalInSeconds));
// define the policy
var retryPolicy =
new RetryPolicy<MyTransientErrorDetectionStrategy>(retryStrategy);
retryPolicy.Retrying += retryPolicy_Retrying;
for (int i = 0; i < 50; i++)
{
// try this a few times just to illustrate
try
{
retryPolicy.ExecuteAction(SomeMethodThatCanSometimesFail);
// (the retry policy has async support as well)
}
catch (Exception)
{
// if it got to this point, your retries were exhausted
// the original exception is rethrown
throw;
}
}
Console.WriteLine("Press Enter to Exit");
Console.ReadLine();
}
private static void SomeMethodThatCanSometimesFail()
{
var random = new Random().Next(1, 4);
if (random == 2)
{
const string msg = "randomFailure";
Console.WriteLine(msg);
throw new Exception(msg);
}
}
private static void retryPolicy_Retrying(object sender, RetryingEventArgs e)
{
Console.WriteLine("retrying");
}
}
}