偶尔我需要在放弃之前多次重试一次手术。我的代码就像:
int retries = 3;
while(true) {
try {
DoSomething();
break; // success!
} catch {
if(--retries == 0) throw;
else Thread.Sleep(1000);
}
}
我想在一般的重试函数中重写它,如:
TryThreeTimes(DoSomething);
C#中有可能吗? TryThreeTimes()
方法的代码是什么?
答案 0 :(得分:522)
public static class Retry
{
public static void Do(
Action action,
TimeSpan retryInterval,
int maxAttemptCount = 3)
{
Do<object>(() =>
{
action();
return null;
}, retryInterval, maxAttemptCount);
}
public static T Do<T>(
Func<T> action,
TimeSpan retryInterval,
int maxAttemptCount = 3)
{
var exceptions = new List<Exception>();
for (int attempted = 0; attempted < maxAttemptCount; attempted++)
{
try
{
if (attempted > 0)
{
Thread.Sleep(retryInterval);
}
return action();
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
throw new AggregateException(exceptions);
}
}
您现在可以使用此实用程序方法执行重试逻辑:
Retry.Do(() => SomeFunctionThatCanFail(), TimeSpan.FromSeconds(1));
或:
Retry.Do(SomeFunctionThatCanFail, TimeSpan.FromSeconds(1));
或:
int result = Retry.Do(SomeFunctionWhichReturnsInt, TimeSpan.FromSeconds(1), 4);
或者你甚至可以使async
超载。
答案 1 :(得分:192)
您应该尝试Polly。这是我编写的一个.NET库,它允许开发人员以流畅的方式表达瞬态异常处理策略,如重试,永远重试,等待和重试或断路器。
Policy
.Handle<SqlException>(ex => ex.Number == 1205)
.Or<ArgumentException>(ex => ex.ParamName == "example")
.WaitAndRetry(3, retryAttempt => TimeSpan.FromSeconds(3))
.Execute(() => DoSomething());
答案 2 :(得分:58)
这可能是一个坏主意。首先,它是“格言疯狂的定义是两次做同样的事情并且每次都期望不同的结果”的格言的象征。其次,这种编码模式本身并不能很好地构成。例如:
假设您的网络硬件层在发生故障时重新发送三次数据包,等待,比如故障之间的第二次。
现在假设软件层在数据包发生故障时重新发送有关故障三次的通知。
现在假设通知层在通知传递失败时重新激活通知三次。
现在假设错误报告层在通知失败时重新激活通知层三次。
现在假设Web服务器在错误失败时重新激活错误报告三次。
现在假设Web服务器在从服务器收到错误后重新发送请求三次。
现在假设网络交换机上应该将通知路由到管理员的线路已拔下。 Web客户端的用户何时最终收到错误消息?我在大约十二分钟后制作它。
为免你认为这只是一个愚蠢的例子:我们在客户代码中看到了这个错误,尽管远比我在这里描述的要糟糕得多。在特定的客户代码中,发生的错误情况与最终报告给用户之间的差距是几个周,因为有很多层自动重试等待。试想一下,如果有十次重试而不是三次会发生什么。
通常正确处理错误的情况是立即报告并让用户决定要做什么。如果用户想要创建自动重试的策略,让他们创建该策略在软件抽象的适当级别。
答案 3 :(得分:41)
public void TryThreeTimes(Action action)
{
var tries = 3;
while (true) {
try {
action();
break; // success!
} catch {
if (--tries == 0)
throw;
Thread.Sleep(1000);
}
}
}
然后你会打电话:
TryThreeTimes(DoSomething);
......或者......
TryThreeTimes(() => DoSomethingElse(withLocalVariable));
更灵活的选择:
public void DoWithRetry(Action action, TimeSpan sleepPeriod, int tryCount = 3)
{
if (tryCount <= 0)
throw new ArgumentOutOfRangeException(nameof(tryCount));
while (true) {
try {
action();
break; // success!
} catch {
if (--tryCount == 0)
throw;
Thread.Sleep(sleepPeriod);
}
}
}
用作:
DoWithRetry(DoSomething, TimeSpan.FromSeconds(2), tryCount: 10);
支持async / await的更现代的版本:
public async Task DoWithRetryAsync(Func<Task> action, TimeSpan sleepPeriod, int tryCount = 3)
{
if (tryCount <= 0)
throw new ArgumentOutOfRangeException(nameof(tryCount));
while (true) {
try {
await action();
return; // success!
} catch {
if (--tryCount == 0)
throw;
await Task.Delay(sleepPeriod);
}
}
}
用作:
await DoWithRetryAsync(DoSomethingAsync, TimeSpan.FromSeconds(2), tryCount: 10);
答案 4 :(得分:32)
Transient Fault Handling Application Block提供了可扩展的重试策略集合,包括:
它还包括一系列基于云的服务的错误检测策略。
有关详细信息,请参阅开发人员指南的this chapter。
可通过NuGet(搜索' topaz ')。
答案 5 :(得分:15)
允许功能并重试消息
public static T RetryMethod<T>(Func<T> method, int numRetries, int retryTimeout, Action onFailureAction)
{
Guard.IsNotNull(method, "method");
T retval = default(T);
do
{
try
{
retval = method();
return retval;
}
catch
{
onFailureAction();
if (numRetries <= 0) throw; // improved to avoid silent failure
Thread.Sleep(retryTimeout);
}
} while (numRetries-- > 0);
return retval;
}
答案 6 :(得分:14)
您可能还会考虑添加要重试的例外类型。例如,这是您要重试的超时异常吗?数据库异常?
RetryForExcpetionType(DoSomething, typeof(TimeoutException), 5, 1000);
public static void RetryForExcpetionType(Action action, Type retryOnExceptionType, int numRetries, int retryTimeout)
{
if (action == null)
throw new ArgumentNullException("action");
if (retryOnExceptionType == null)
throw new ArgumentNullException("retryOnExceptionType");
while (true)
{
try
{
action();
return;
}
catch(Exception e)
{
if (--numRetries <= 0 || !retryOnExceptionType.IsAssignableFrom(e.GetType()))
throw;
if (retryTimeout > 0)
System.Threading.Thread.Sleep(retryTimeout);
}
}
}
您可能还会注意到,所有其他示例在测试重试次数== 0时都存在类似问题,并且在给定负值时重试无穷大或无法引发异常。 Sleep(-1000)也会在上面的catch块中失败。取决于你对人们的期待是多么“愚蠢”,但防御性编程永远不会受到伤害。
答案 7 :(得分:10)
我是递归和扩展方法的粉丝,所以这是我的两分钱:
public static void InvokeWithRetries(this Action @this, ushort numberOfRetries)
{
try
{
@this();
}
catch
{
if (numberOfRetries == 0)
throw;
InvokeWithRetries(@this, --numberOfRetries);
}
}
答案 8 :(得分:7)
在以前的工作基础上,我考虑过以三种方式增强重试逻辑:
将其设为Action
扩展方法
static class ActionExtensions
{
public static void InvokeAndRetryOnException<T> (this Action action, int retries, TimeSpan retryDelay) where T : Exception
{
if (action == null)
throw new ArgumentNullException("action");
while( retries-- > 0 )
{
try
{
action( );
return;
}
catch (T)
{
Thread.Sleep( retryDelay );
}
}
action( );
}
}
然后可以像这样调用该方法(当然也可以使用匿名方法):
new Action( AMethodThatMightThrowIntermittentException )
.InvokeAndRetryOnException<IntermittentException>( 2, TimeSpan.FromSeconds( 1 ) );
答案 9 :(得分:5)
使用C#6.0保持简单
public async Task<T> Retry<T>(Func<T> action, TimeSpan retryInterval, int retryCount)
{
try
{
return action();
}
catch when (retryCount != 0)
{
await Task.Delay(retryInterval);
return await Retry(action, retryInterval, --retryCount);
}
}
答案 10 :(得分:5)
使用Polly
https://github.com/App-vNext/Polly-Samples
这是我与Polly一起使用的重试 - 通用
public T Retry<T>(Func<T> action, int retryCount = 0)
{
PolicyResult<T> policyResult = Policy
.Handle<Exception>()
.Retry(retryCount)
.ExecuteAndCapture<T>(action);
if (policyResult.Outcome == OutcomeType.Failure)
{
throw policyResult.FinalException;
}
return policyResult.Result;
}
像这样使用
var result = Retry(() => MyFunction()), 3);
答案 11 :(得分:4)
以最新的方式实施了LBushkin的答案:
melted_df = pd.melt(tabledf, id_vars = "IDs").sort_values('IDs').reset_index(drop=True)
print(melted_df)
# IDs variable value
# 0 Ellie AABC 12.00
# 1 Ellie ABC6 NaN
# 2 Ellie YHG.8 48.70
# 3 Ellie D78Ha 33.00
# 4 Joe AABC 22.00
# 5 Joe D78Ha NaN
# 6 Joe ABC6 53.00
# 7 Joe YHG.8 49.00
# 8 Kate AABC 98.00
# 9 Kate ABC6 34.00
# 10 Kate YHG.8 21.00
# 11 Kate D78Ha 76.36
# 12 Van AABC 77.00
# 13 Van ABC6 NaN
# 14 Van D78Ha 12.10
# 15 Van YHG.8 40.00
# 16 Xavier ABC6 NaN
# 17 Xavier AABC NaN
# 18 Xavier YHG.8 NaN
# 19 Xavier D78Ha 88.80
并使用它:
public static async Task Do(Func<Task> task, TimeSpan retryInterval, int maxAttemptCount = 3)
{
var exceptions = new List<Exception>();
for (int attempted = 0; attempted < maxAttemptCount; attempted++)
{
try
{
if (attempted > 0)
{
await Task.Delay(retryInterval);
}
await task();
return;
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
throw new AggregateException(exceptions);
}
public static async Task<T> Do<T>(Func<Task<T>> task, TimeSpan retryInterval, int maxAttemptCount = 3)
{
var exceptions = new List<Exception>();
for (int attempted = 0; attempted < maxAttemptCount; attempted++)
{
try
{
if (attempted > 0)
{
await Task.Delay(retryInterval);
}
return await task();
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
throw new AggregateException(exceptions);
}
而函数await Retry.Do([TaskFunction], retryInterval, retryAttempts);
可以是[TaskFunction]
,也可以是Task<T>
。
答案 12 :(得分:4)
我实现了这个:
public static bool Retry(int maxRetries, Func<bool, bool> method)
{
while (maxRetries > 0)
{
if (method(maxRetries == 1))
{
return true;
}
maxRetries--;
}
return false;
}
我不会像在其他示例中那样使用异常。在我看来,如果我们期待一种方法不会成功的可能性,它的失败也不例外。所以我调用的方法如果成功则返回true,如果失败则返回false。
为什么它是Func<bool, bool>
而不只是Func<bool>
?因此,如果我希望一个方法能够在失败时抛出异常,我有办法通知它这是最后一次尝试。
所以我可以使用代码如:
Retry(5, delegate(bool lastIteration)
{
// do stuff
if (!succeeded && lastIteration)
{
throw new InvalidOperationException(...)
}
return succeeded;
});
或
if (!Retry(5, delegate(bool lastIteration)
{
// do stuff
return succeeded;
}))
{
Console.WriteLine("Well, that didn't work.");
}
如果传递方法未使用的参数证明是笨拙的,那么实现Retry
的重载只需要Func<bool>
也是微不足道的。
答案 13 :(得分:2)
此方法允许重试某些异常类型(立即引发其他异常)。
public static void DoRetry(
List<Type> retryOnExceptionTypes,
Action actionToTry,
int retryCount = 5,
int msWaitBeforeEachRety = 300)
{
for (var i = 0; i < retryCount; ++i)
{
try
{
actionToTry();
break;
}
catch (Exception ex)
{
// Retries exceeded
// Throws on last iteration of loop
if (i == retryCount - 1) throw;
// Is type retryable?
var exceptionType = ex.GetType();
if (!retryOnExceptionTypes.Contains(exceptionType))
{
throw;
}
// Wait before retry
Thread.Sleep(msWaitBeforeEachRety);
}
}
}
public static void DoRetry(
Type retryOnExceptionType,
Action actionToTry,
int retryCount = 5,
int msWaitBeforeEachRety = 300)
=> DoRetry(new List<Type> {retryOnExceptionType}, actionToTry, retryCount, msWaitBeforeEachRety);
用法示例:
DoRetry(typeof(IOException), () => {
using (var fs = new FileStream(requestedFilePath, FileMode.Create, FileAccess.Write))
{
fs.Write(entryBytes, 0, entryBytes.Length);
}
});
答案 14 :(得分:2)
对于那些希望同时重试任何异常或显式设置异常类型的用户,请使用:
public class RetryManager
{
public void Do(Action action,
TimeSpan interval,
int retries = 3)
{
Try<object, Exception>(() => {
action();
return null;
}, interval, retries);
}
public T Do<T>(Func<T> action,
TimeSpan interval,
int retries = 3)
{
return Try<T, Exception>(
action
, interval
, retries);
}
public T Do<E, T>(Func<T> action,
TimeSpan interval,
int retries = 3) where E : Exception
{
return Try<T, E>(
action
, interval
, retries);
}
public void Do<E>(Action action,
TimeSpan interval,
int retries = 3) where E : Exception
{
Try<object, E>(() => {
action();
return null;
}, interval, retries);
}
private T Try<T, E>(Func<T> action,
TimeSpan interval,
int retries = 3) where E : Exception
{
var exceptions = new List<E>();
for (int retry = 0; retry < retries; retry++)
{
try
{
if (retry > 0)
Thread.Sleep(interval);
return action();
}
catch (E ex)
{
exceptions.Add(ex);
}
}
throw new AggregateException(exceptions);
}
}
答案 15 :(得分:1)
我使用 Polly 实现了这种模式的两个实现。一种是异步的。
我的同步方法基于 this answer 的 Erik Bergstedt
public static T Retry<T>(Func<T> action, TimeSpan retryWait, int retryCount = 0)
{
PolicyResult<T> policyResult = Policy
.Handle<ApiException>(ex => ex.ResponseCode == (int)HttpStatusCode.TooManyRequests)
.WaitAndRetry(retryCount, retryAttempt => retryWait)
.ExecuteAndCapture(action);
if (policyResult.Outcome == OutcomeType.Failure)
{
throw policyResult.FinalException;
}
return policyResult.Result;
}
异步:
public static async Task<T> RetryAsync<T>(Func<Task<T>> action, TimeSpan retryWait, int retryCount = 0)
{
PolicyResult<T> policyResult = await Policy
.Handle<ApiException>(ex => ex.ResponseCode == (int)HttpStatusCode.TooManyRequests)
.WaitAndRetryAsync(retryCount, retryAttempt => retryWait)
.ExecuteAndCaptureAsync(action);
if (policyResult.Outcome == OutcomeType.Failure)
{
throw policyResult.FinalException;
}
return policyResult.Result;
}
允许传入异常类型以及异常类型的 lambda 也很容易。
答案 16 :(得分:1)
Exponential backoff是一个很好的重试策略,而不是简单地尝试x次。您可以使用像Polly这样的库来实现它。
答案 17 :(得分:1)
我的async
重试方法的实现:
public static async Task<T> DoAsync<T>(Func<dynamic> action, TimeSpan retryInterval, int retryCount = 3)
{
var exceptions = new List<Exception>();
for (int retry = 0; retry < retryCount; retry++)
{
try
{
return await action().ConfigureAwait(false);
}
catch (Exception ex)
{
exceptions.Add(ex);
}
await Task.Delay(retryInterval).ConfigureAwait(false);
}
throw new AggregateException(exceptions);
}
关键点:我使用.ConfigureAwait(false);
和Func<dynamic>
代替Func<T>
答案 18 :(得分:1)
我需要一种支持取消的方法,而在此期间,我添加了对返回中间故障的支持。
public static class ThreadUtils
{
public static RetryResult Retry(
Action target,
CancellationToken cancellationToken,
int timeout = 5000,
int retries = 0)
{
CheckRetryParameters(timeout, retries)
var failures = new List<Exception>();
while(!cancellationToken.IsCancellationRequested)
{
try
{
target();
return new RetryResult(failures);
}
catch (Exception ex)
{
failures.Add(ex);
}
if (retries > 0)
{
retries--;
if (retries == 0)
{
throw new AggregateException(
"Retry limit reached, see InnerExceptions for details.",
failures);
}
}
if (cancellationToken.WaitHandle.WaitOne(timeout))
{
break;
}
}
failures.Add(new OperationCancelledException(
"The Retry Operation was cancelled."));
throw new AggregateException("Retry was cancelled.", failures);
}
private static void CheckRetryParameters(int timeout, int retries)
{
if (timeout < 1)
{
throw new ArgumentOutOfRangeException(...
}
if (retries < 0)
{
throw new ArgumentOutOfRangeException(...
}
}
public class RetryResult : IEnumerable<Exception>
{
private readonly IEnumerable<Exception> failureExceptions;
private readonly int failureCount;
protected internal RetryResult(
ICollection<Exception> failureExceptions)
{
this.failureExceptions = failureExceptions;
this.failureCount = failureExceptions.Count;
}
}
public int FailureCount
{
get { return this.failureCount; }
}
public IEnumerator<Exception> GetEnumerator()
{
return this.failureExceptions.GetEnumerator();
}
System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
您可以像这样使用Retry
功能,重试3次,延迟10秒但不取消。
try
{
var result = ThreadUtils.Retry(
SomeAction,
CancellationToken.None,
10000,
3);
// it worked
result.FailureCount // but failed this many times first.
}
catch (AggregationException ex)
{
// oops, 3 retries wasn't enough.
}
或者,除非取消,否则每五秒钟重试一次。
try
{
var result = ThreadUtils.Retry(
SomeAction,
someTokenSource.Token);
// it worked
result.FailureCount // but failed this many times first.
}
catch (AggregationException ex)
{
// operation was cancelled before success.
}
你可以猜到,在我的源代码中,我重载了Retry
函数来支持我想要使用的不同delgate类型。
答案 19 :(得分:0)
我知道这个答案很老但我只是想对此发表评论,因为我在使用这些问题时遇到了问题。
多年来,我认为我已经采取了更好的方法。即使用某种事件聚合,如反应性扩展“主题”等。当尝试失败时,您只需发布一个表示尝试失败的事件,并让聚合器函数重新安排该事件。这允许您更多地控制重试,而不会通过一堆重试循环来污染调用本身,而不是。你也没有用一堆线程睡眠来捆绑一个线程。
答案 20 :(得分:0)
int retries = 3;
while (true)
{
try
{
//Do Somthing
break;
}
catch (Exception ex)
{
if (--retries == 0)
return Request.BadRequest(ApiUtil.GenerateRequestResponse(false, "3 Times tried it failed do to : " + ex.Message, new JObject()));
else
System.Threading.Thread.Sleep(100);
}
答案 21 :(得分:0)
我会将以下代码添加到接受的答案
sounds
基本上,上面的代码使public static class Retry<TException> where TException : Exception //ability to pass the exception type
{
//same code as the accepted answer ....
public static T Do<T>(Func<T> action, TimeSpan retryInterval, int retryCount = 3)
{
var exceptions = new List<Exception>();
for (int retry = 0; retry < retryCount; retry++)
{
try
{
return action();
}
catch (TException ex) //Usage of the exception type
{
exceptions.Add(ex);
Thread.Sleep(retryInterval);
}
}
throw new AggregateException(String.Format("Failed to excecute after {0} attempt(s)", retryCount), exceptions);
}
}
类变得通用,因此您可以传递要捕获的异常类型以进行重试。
现在几乎以相同的方式使用它,但指定异常类型
Retry
答案 22 :(得分:0)
我需要将一些参数传递给我的方法来重试,并且有一个结果值;所以我需要一个表达.. 我建立了这个完成工作的课程(它受到了LBushkin的启发) 你可以像这样使用它:
static void Main(string[] args)
{
// one shot
var res = Retry<string>.Do(() => retryThis("try"), 4, TimeSpan.FromSeconds(2), fix);
// delayed execute
var retry = new Retry<string>(() => retryThis("try"), 4, TimeSpan.FromSeconds(2), fix);
var res2 = retry.Execute();
}
static void fix()
{
Console.WriteLine("oh, no! Fix and retry!!!");
}
static string retryThis(string tryThis)
{
Console.WriteLine("Let's try!!!");
throw new Exception(tryThis);
}
public class Retry<TResult>
{
Expression<Func<TResult>> _Method;
int _NumRetries;
TimeSpan _RetryTimeout;
Action _OnFailureAction;
public Retry(Expression<Func<TResult>> method, int numRetries, TimeSpan retryTimeout, Action onFailureAction)
{
_Method = method;
_NumRetries = numRetries;
_OnFailureAction = onFailureAction;
_RetryTimeout = retryTimeout;
}
public TResult Execute()
{
TResult result = default(TResult);
while (_NumRetries > 0)
{
try
{
result = _Method.Compile()();
break;
}
catch
{
_OnFailureAction();
_NumRetries--;
if (_NumRetries <= 0) throw; // improved to avoid silent failure
Thread.Sleep(_RetryTimeout);
}
}
return result;
}
public static TResult Do(Expression<Func<TResult>> method, int numRetries, TimeSpan retryTimeout, Action onFailureAction)
{
var retry = new Retry<TResult>(method, numRetries, retryTimeout, onFailureAction);
return retry.Execute();
}
}
PS。 LBushkin的解决方案再做一次重试= D
答案 23 :(得分:0)
import java.util.function.Supplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RetryHelper {
private static final Logger log = LoggerFactory.getLogger(RetryHelper.class);
private int retryWaitInMS;
private int maxRetries;
public RetryHelper() {
this.retryWaitInMS = 300;
this.maxRetries = 3;
}
public RetryHelper(int maxRetry) {
this.maxRetries = maxRetry;
this.retryWaitInMS = 300;
}
public RetryHelper(int retryWaitInSeconds, int maxRetry) {
this.retryWaitInMS = retryWaitInSeconds;
this.maxRetries = maxRetry;
}
public <T> T retryAndReturn(Supplier<T> supplier) {
try {
return supplier.get();
} catch (Exception var3) {
return this.retrySupplier(supplier);
}
}
public void retry(Runnable runnable) {
try {
runnable.run();
} catch (Exception var3) {
this.retrySupplier(() -> {
runnable.run();
return null;
});
}
}
private <T> T retrySupplier(Supplier<T> supplier) {
log.error("Failed <TASK>, will be retried " + this.maxRetries + " times.");
int retryCounter = 0;
while(retryCounter < this.maxRetries) {
try {
return supplier.get();
} catch (Exception var6) {
++retryCounter;
log.error("<TASK> failed on retry: " + retryCounter + " of " + this.maxRetries + " with error: " + var6.getMessage());
if (retryCounter >= this.maxRetries) {
log.error("Max retries exceeded.");
throw var6;
}
try {
Thread.sleep((long)this.retryWaitInMS);
} catch (InterruptedException var5) {
var5.printStackTrace();
}
}
}
return supplier.get();
}
public int getRetryWaitInMS() {
return this.retryWaitInMS;
}
public int getMaxRetries() {
return this.maxRetries;
}
}
try {
returnValue = new RetryHelper().retryAndReturn(() -> performSomeTask(args));
//or no return type:
new RetryHelper().retry(() -> mytask(args));
} catch(Exception ex){
log.error(e.getMessage());
throw new CustomException();
}
答案 24 :(得分:0)
或者做得有点整洁......
int retries = 3;
while (retries > 0)
{
if (DoSomething())
{
retries = 0;
}
else
{
retries--;
}
}
我认为通常应该避免抛出异常作为一种机制,除非你将它们传递到边界之间(例如构建其他人可以使用的库)。为什么不让DoSomething()
命令成功返回true
,否则返回false
?
编辑:这可以封装在其他人建议的功能中。唯一的问题是如果您没有自己编写DoSomething()
函数
答案 25 :(得分:0)
这是一个async
/ await
版本,该版本汇总了异常并支持取消。
/// <seealso href="https://docs.microsoft.com/en-us/azure/architecture/patterns/retry"/>
protected static async Task<T> DoWithRetry<T>( Func<Task<T>> action, CancellationToken cancelToken, int maxRetries = 3 )
{
var exceptions = new List<Exception>();
for ( int retries = 0; !cancelToken.IsCancellationRequested; retries++ )
try {
return await action().ConfigureAwait( false );
} catch ( Exception ex ) {
exceptions.Add( ex );
if ( retries < maxRetries )
await Task.Delay( 500, cancelToken ).ConfigureAwait( false ); //ease up a bit
else
throw new AggregateException( "Retry limit reached", exceptions );
}
exceptions.Add( new OperationCanceledException( cancelToken ) );
throw new AggregateException( "Retry loop was canceled", exceptions );
}
答案 26 :(得分:-1)
我根据这里发布的答案写了一个小班。希望它会帮助某人:https://github.com/natenho/resiliency
using System;
using System.Threading;
/// <summary>
/// Classe utilitária para suporte a resiliência
/// </summary>
public sealed class Resiliency
{
/// <summary>
/// Define o valor padrão de número de tentativas
/// </summary>
public static int DefaultRetryCount { get; set; }
/// <summary>
/// Define o valor padrão (em segundos) de tempo de espera entre tentativas
/// </summary>
public static int DefaultRetryTimeout { get; set; }
/// <summary>
/// Inicia a parte estática da resiliência, com os valores padrões
/// </summary>
static Resiliency()
{
DefaultRetryCount = 3;
DefaultRetryTimeout = 0;
}
/// <summary>
/// Executa uma <see cref="Action"/> e tenta novamente DefaultRetryCount vezes quando for disparada qualquer <see cref="Exception"/>
/// </summary>
/// <param name="action">Ação a ser realizada</param>
/// <remarks>Executa uma vez e realiza outras DefaultRetryCount tentativas em caso de exceção. Não aguarda para realizar novas tentativa.</remarks>
public static void Try(Action action)
{
Try<Exception>(action, DefaultRetryCount, TimeSpan.FromMilliseconds(DefaultRetryTimeout), null);
}
/// <summary>
/// Executa uma <see cref="Action"/> e tenta novamente determinado número de vezes quando for disparada qualquer <see cref="Exception"/>
/// </summary>
/// <param name="action">Ação a ser realizada</param>
/// <param name="retryCount">Número de novas tentativas a serem realizadas</param>
/// <param name="retryTimeout">Tempo de espera antes de cada nova tentativa</param>
public static void Try(Action action, int retryCount, TimeSpan retryTimeout)
{
Try<Exception>(action, retryCount, retryTimeout, null);
}
/// <summary>
/// Executa uma <see cref="Action"/> e tenta novamente determinado número de vezes quando for disparada qualquer <see cref="Exception"/>
/// </summary>
/// <param name="action">Ação a ser realizada</param>
/// <param name="retryCount">Número de novas tentativas a serem realizadas</param>
/// <param name="retryTimeout">Tempo de espera antes de cada nova tentativa</param>
/// <param name="tryHandler">Permitindo manipular os critérios para realizar as tentativas</param>
public static void Try(Action action, int retryCount, TimeSpan retryTimeout, Action<ResiliencyTryHandler<Exception>> tryHandler)
{
Try<Exception>(action, retryCount, retryTimeout, tryHandler);
}
/// <summary>
/// Executa uma <see cref="Action"/> e tenta novamente por até DefaultRetryCount vezes quando for disparada qualquer <see cref="Exception"/>
/// </summary>
/// <param name="action">Ação a ser realizada</param>
/// <param name="tryHandler">Permitindo manipular os critérios para realizar as tentativas</param>
/// <remarks>Executa uma vez e realiza outras DefaultRetryCount tentativas em caso de exceção. Aguarda DefaultRetryTimeout segundos antes de realizar nova tentativa.</remarks>
public static void Try(Action action, Action<ResiliencyTryHandler<Exception>> tryHandler)
{
Try<Exception>(action, DefaultRetryCount, TimeSpan.FromSeconds(DefaultRetryTimeout), null);
}
/// <summary>
/// Executa uma <see cref="Action"/> e tenta novamente determinado número de vezes quando for disparada qualquer <see cref="TException"/>
/// </summary>
/// <param name="action">Ação a ser realizada</param>
/// <remarks>Executa uma vez e realiza outras DefaultRetryCount tentativas em caso de exceção. Aguarda DefaultRetryTimeout segundos antes de realizar nova tentativa.</remarks>
public static void Try<TException>(Action action) where TException : Exception
{
Try<TException>(action, DefaultRetryCount, TimeSpan.FromSeconds(DefaultRetryTimeout), null);
}
/// <summary>
/// Executa uma <see cref="Action"/> e tenta novamente determinado número de vezes quando for disparada qualquer <see cref="TException"/>
/// </summary>
/// <param name="action">Ação a ser realizada</param>
/// <param name="retryCount"></param>
public static void Try<TException>(Action action, int retryCount) where TException : Exception
{
Try<TException>(action, retryCount, TimeSpan.FromSeconds(DefaultRetryTimeout), null);
}
/// <summary>
/// Executa uma <see cref="Action"/> e tenta novamente determinado número de vezes quando for disparada qualquer <see cref="Exception"/>
/// </summary>
/// <param name="action">Ação a ser realizada</param>
/// <param name="retryCount"></param>
/// <param name="retryTimeout"></param>
public static void Try<TException>(Action action, int retryCount, TimeSpan retryTimeout) where TException : Exception
{
Try<TException>(action, retryCount, retryTimeout, null);
}
/// <summary>
/// Executa uma <see cref="Action"/> e tenta novamente determinado número de vezes quando for disparada qualquer <see cref="Exception"/>
/// </summary>
/// <param name="action">Ação a ser realizada</param>
/// <param name="tryHandler">Permitindo manipular os critérios para realizar as tentativas</param>
/// <remarks>Executa uma vez e realiza outras DefaultRetryCount tentativas em caso de exceção. Aguarda DefaultRetryTimeout segundos antes de realizar nova tentativa.</remarks>
public static void Try<TException>(Action action, Action<ResiliencyTryHandler<TException>> tryHandler) where TException : Exception
{
Try(action, DefaultRetryCount, TimeSpan.FromSeconds(DefaultRetryTimeout), tryHandler);
}
/// <summary>
/// Executa uma <see cref="Action"/> e tenta novamente determinado número de vezes quando for disparada uma <see cref="Exception"/> definida no tipo genérico
/// </summary>
/// <param name="action">Ação a ser realizada</param>
/// <param name="retryCount">Número de novas tentativas a serem realizadas</param>
/// <param name="retryTimeout">Tempo de espera antes de cada nova tentativa</param>
/// <param name="tryHandler">Permitindo manipular os critérios para realizar as tentativas</param>
/// <remarks>Construído a partir de várias ideias no post <seealso cref="http://stackoverflow.com/questions/156DefaultRetryCount191/c-sharp-cleanest-way-to-write-retry-logic"/></remarks>
public static void Try<TException>(Action action, int retryCount, TimeSpan retryTimeout, Action<ResiliencyTryHandler<TException>> tryHandler) where TException : Exception
{
if (action == null)
throw new ArgumentNullException(nameof(action));
while (retryCount-- > 0)
{
try
{
action();
return;
}
catch (TException ex)
{
//Executa o manipulador de exception
if (tryHandler != null)
{
var callback = new ResiliencyTryHandler<TException>(ex, retryCount);
tryHandler(callback);
//A propriedade que aborta pode ser alterada pelo cliente
if (callback.AbortRetry)
throw;
}
//Aguarda o tempo especificado antes de tentar novamente
Thread.Sleep(retryTimeout);
}
}
//Na última tentativa, qualquer exception será lançada de volta ao chamador
action();
}
}
/// <summary>
/// Permite manipular o evento de cada tentativa da classe de <see cref="Resiliency"/>
/// </summary>
public class ResiliencyTryHandler<TException> where TException : Exception
{
#region Properties
/// <summary>
/// Opção para abortar o ciclo de tentativas
/// </summary>
public bool AbortRetry { get; set; }
/// <summary>
/// <see cref="Exception"/> a ser tratada
/// </summary>
public TException Exception { get; private set; }
/// <summary>
/// Identifca o número da tentativa atual
/// </summary>
public int CurrentTry { get; private set; }
#endregion
#region Constructors
/// <summary>
/// Instancia um manipulador de tentativa. É utilizado internamente
/// por <see cref="Resiliency"/> para permitir que o cliente altere o
/// comportamento do ciclo de tentativas
/// </summary>
public ResiliencyTryHandler(TException exception, int currentTry)
{
Exception = exception;
CurrentTry = currentTry;
}
#endregion
}
答案 27 :(得分:-1)
public delegate void ThingToTryDeletage();
public static void TryNTimes(ThingToTryDelegate, int N, int sleepTime)
{
while(true)
{
try
{
ThingToTryDelegate();
} catch {
if( --N == 0) throw;
else Thread.Sleep(time);
}
}
答案 28 :(得分:-2)
我已经实现了已接受答案的异步版本,如此-似乎效果很好-有任何评论吗?
public static async Task DoAsync(
Action action,
TimeSpan retryInterval,
int maxAttemptCount = 3)
{
DoAsync<object>(() =>
{
action();
return null;
}, retryInterval, maxAttemptCount);
}
public static async Task<T> DoAsync<T>(
Func<Task<T>> action,
TimeSpan retryInterval,
int maxAttemptCount = 3)
{
var exceptions = new List<Exception>();
for (int attempted = 0; attempted < maxAttemptCount; attempted++)
{
try
{
if (attempted > 0)
{
Thread.Sleep(retryInterval);
}
return await action();
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
throw new AggregateException(exceptions);
}
然后,简单地这样称呼它:
var result = await Retry.DoAsync(() => MyAsyncMethod(), TimeSpan.FromSeconds(5), 4);