偶尔我需要在放弃之前多次重试一次手术.我的代码是这样的:
int retries = 3;
while(true) {
try {
DoSomething();
break; // success!
} catch {
if(--retries == 0) throw;
else Thread.Sleep(1000);
}
}
Run Code Online (Sandbox Code Playgroud)
我想在一般的重试函数中重写它,如:
TryThreeTimes(DoSomething);
Run Code Online (Sandbox Code Playgroud)
在C#中有可能吗?该TryThreeTimes()
方法的代码是什么?
LBu*_*kin 540
如果用作一般异常处理机制,那么简单地重试相同调用的Blanket catch语句可能是危险的.话虽如此,这是一个基于lambda的重试包装器,您可以使用任何方法.我选择将重试次数和重试超时作为参数进行分解,以获得更大的灵活性:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
您现在可以使用此实用程序方法执行重试逻辑:
Retry.Do(() => SomeFunctionThatCanFail(), TimeSpan.FromSeconds(1));
Run Code Online (Sandbox Code Playgroud)
要么:
Retry.Do(SomeFunctionThatCanFail, TimeSpan.FromSeconds(1));
Run Code Online (Sandbox Code Playgroud)
要么:
int result = Retry.Do(SomeFunctionWhichReturnsInt, TimeSpan.FromSeconds(1), 4);
Run Code Online (Sandbox Code Playgroud)
或者你甚至可以async
超载.
Mic*_*den 208
你应该试试波莉.它是我编写的.NET库,允许开发人员以流畅的方式表达瞬态异常处理策略,如重试,重试永久,等待和重试或断路器.
Policy
.Handle<SqlException>(ex => ex.Number == 1205)
.Or<ArgumentException>(ex => ex.ParamName == "example")
.WaitAndRetry(3, retryAttempt => TimeSpan.FromSeconds(3))
.Execute(() => DoSomething());
Run Code Online (Sandbox Code Playgroud)
Eri*_*ert 59
这可能是一个坏主意.首先,它是"格言疯狂的定义是两次做同样的事情并且每次都期望不同的结果"的格言的象征.其次,这种编码模式本身并不能很好地构成.例如:
假设您的网络硬件层在发生故障时重新发送三次数据包,等待,比如故障之间的第二次.
现在假设软件层在数据包故障时重新发送有关故障三次的通知.
现在假设通知层在通知传递失败时重新激活通知三次.
现在假设错误报告层在通知失败时重新激活通知层三次.
现在假设Web服务器在错误失败时重新激活错误报告三次.
现在假设Web客户端在从服务器收到错误时重新发送请求三次.
现在假设网络交换机上应该将通知路由到管理员的线路已拔下.Web客户端的用户何时最终收到错误消息?我在大约十二分钟后制作它.
以免你认为这只是一个愚蠢的例子:我们在客户代码中看到了这个错误,尽管远比我在这里描述的要糟糕得多.在特定的客户代码中,发生的错误情况与最终报告给用户之间的差距是几周,因为很多层都在自动重试等待.试想一下,如果有十次重试而不是三次重试会发生什么.
通常,对错误条件做正确的事情是立即报告并让用户决定做什么. 如果用户想要创建自动重试策略,请让他们在软件抽象中的适当级别创建该策略.
Dre*_*kes 44
public void TryThreeTimes(Action action)
{
var tries = 3;
while (true) {
try {
action();
break; // success!
} catch {
if (--tries == 0)
throw;
Thread.Sleep(1000);
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后你会打电话:
TryThreeTimes(DoSomething);
Run Code Online (Sandbox Code Playgroud)
......或者......
TryThreeTimes(() => DoSomethingElse(withLocalVariable));
Run Code Online (Sandbox Code Playgroud)
更灵活的选择:
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
用作:
DoWithRetry(DoSomething, TimeSpan.FromSeconds(2), tryCount: 10);
Run Code Online (Sandbox Code Playgroud)
一个更现代的版本,支持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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
用作:
await DoWithRetryAsync(DoSomethingAsync, TimeSpan.FromSeconds(2), tryCount: 10);
Run Code Online (Sandbox Code Playgroud)
Gri*_*nik 32
该瞬时故障处理应用程序块提供的重试策略,包括一个可扩展的集合:
它还包括一系列基于云的服务的错误检测策略.
有关更多信息,请参阅开发人员指南的本章.
可通过NuGet获取(搜索' topaz ').
Bri*_*ian 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;
}
Run Code Online (Sandbox Code Playgroud)
csh*_*net 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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
您可能还会注意到,所有其他示例在测试重试次数== 0时都存在类似问题,并且在给定负值时重试无穷大或未能引发异常.Sleep(-1000)也会在上面的catch块中失败.取决于你对人们的期待是多么"愚蠢",但防御性编程永远不会受到伤害.
Mar*_*R-L 11
我是递归和扩展方法的粉丝,所以这是我的两分钱:
public static void InvokeWithRetries(this Action @this, ushort numberOfRetries)
{
try
{
@this();
}
catch
{
if (numberOfRetries == 0)
throw;
InvokeWithRetries(@this, --numberOfRetries);
}
}
Run Code Online (Sandbox Code Playgroud)
在前面的工作基础上,我考虑过以三种方式增强重试逻辑:
使其成为一种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( );
}
}
Run Code Online (Sandbox Code Playgroud)然后可以像这样调用该方法(当然也可以使用匿名方法):
new Action( AMethodThatMightThrowIntermittentException )
.InvokeAndRetryOnException<IntermittentException>( 2, TimeSpan.FromSeconds( 1 ) );
Run Code Online (Sandbox Code Playgroud)
使用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;
}
Run Code Online (Sandbox Code Playgroud)
像这样使用它
var result = Retry(() => MyFunction()), 3);
Run Code Online (Sandbox Code Playgroud)
使用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);
}
}
Run Code Online (Sandbox Code Playgroud)
以最新方式实施LBushkin的答案:
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);
}
Run Code Online (Sandbox Code Playgroud)
并使用它:
await Retry.Do([TaskFunction], retryInterval, retryAttempts);
Run Code Online (Sandbox Code Playgroud)
而函数[TaskFunction]
可以是Task<T>
或Task
。
我使用 Polly 有两个该模式的实现。一种是异步的。
我的同步方法基于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;
}
Run Code Online (Sandbox Code Playgroud)
异步:
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;
}
Run Code Online (Sandbox Code Playgroud)
允许传入异常类型以及异常类型的 lambda 也很容易。