异常过滤器使catch
子句更加简单:
private static async Task<T> Retry<T>(Func<T> func, int retryCount)
{
while (true)
{
try
{
var result = await Task.Run(func);
return result;
}
catch when (retryCount-- > 0){}
}
}
和递归版本:
private static async Task<T> Retry<T>(Func<T> func, int retryCount)
{
try
{
var result = await Task.Run(func);
return result;
}
catch when (retryCount-- > 0){}
return await Retry(func, retryCount);
}
编码Retry函数的方法有很多种:您可以使用递归或任务迭代。一段时间以来,希腊.NET用户组中进行了讨论,讨论了执行此操作的不同方法。 如果使用的是F#,则还可以使用异步构造。不幸的是,您至少不能在异步CTP中使用async / await构造,因为编译器生成的代码不喜欢多次等待或在catch块中重新抛出。
递归版本可能是在C#中构建Retry的最简单方法。以下版本不使用Unwrap,而是在重试之前添加了可选的延迟:
private static Task<T> Retry<T>(Func<T> func, int retryCount, int delay, TaskCompletionSource<T> tcs = null)
{
if (tcs == null)
tcs = new TaskCompletionSource<T>();
Task.Factory.StartNew(func).ContinueWith(_original =>
{
if (_original.IsFaulted)
{
if (retryCount == 0)
tcs.SetException(_original.Exception.InnerExceptions);
else
Task.Factory.StartNewDelayed(delay).ContinueWith(t =>
{
Retry(func, retryCount - 1, delay,tcs);
});
}
else
tcs.SetResult(_original.Result);
});
return tcs.Task;
}
所述StartNewDelayed函数来自ParallelExtensionsExtras样品和使用定时器发生超时时,以触发TaskCompletionSource。
F#版本要简单得多:
let retry (asynccomputation : Async<'T>) (retryCount : int) : Async<'T> =
let rec retry' retryCount =
async {
try
let! result = asynccomputation
return result
with exn ->
if retryCount = 0 then
return raise exn
else
return! retry' (retryCount - 1)
}
retry' retryCount
不幸的是,不可能从Async CTP使用async / await在C#中写类似的东西,因为编译器不喜欢catch块中的await语句。以下尝试也无法静默失败,因为运行时不喜欢在异常后遇到等待:
private static async Task<T> Retry<T>(Func<T> func, int retryCount)
{
while (true)
{
try
{
var result = await TaskEx.Run(func);
return result;
}
catch
{
if (retryCount == 0)
throw;
retryCount--;
}
}
}
至于询问用户,您可以修改重试以调用询问用户并通过TaskCompletionSource返回任务的函数,以在用户回答时触发下一步,例如:
private static Task<bool> AskUser()
{
var tcs = new TaskCompletionSource<bool>();
Task.Factory.StartNew(() =>
{
Console.WriteLine(@"Error Occured, continue? Y\N");
var response = Console.ReadKey();
tcs.SetResult(response.KeyChar=='y');
});
return tcs.Task;
}
private static Task<T> RetryAsk<T>(Func<T> func, int retryCount, TaskCompletionSource<T> tcs = null)
{
if (tcs == null)
tcs = new TaskCompletionSource<T>();
Task.Factory.StartNew(func).ContinueWith(_original =>
{
if (_original.IsFaulted)
{
if (retryCount == 0)
tcs.SetException(_original.Exception.InnerExceptions);
else
AskUser().ContinueWith(t =>
{
if (t.Result)
RetryAsk(func, retryCount - 1, tcs);
});
}
else
tcs.SetResult(_original.Result);
});
return tcs.Task;
}
通过所有后续操作,您可以了解为什么非常需要异步版本的Retry。
在Visual Studio 2012 Beta中,以下两个版本适用:
一个带有while循环的版本:
private static async Task<T> Retry<T>(Func<T> func, int retryCount)
{
while (true)
{
try
{
var result = await Task.Run(func);
return result;
}
catch
{
if (retryCount == 0)
throw;
retryCount--;
}
}
}
和递归版本:
private static async Task<T> Retry<T>(Func<T> func, int retryCount)
{
try
{
var result = await Task.Run(func);
return result;
}
catch
{
if (retryCount == 0)
throw;
}
return await Retry(func, --retryCount);
}