gpt4 book ai didi

c# - 在任务异常的情况下,根据用户输入多次重试任务

转载 作者:可可西里 更新时间:2023-11-01 08:00:42 26 4
gpt4 key购买 nike

我的应用程序中的所有服务调用都是作为任务实现的。当任务出现故障时,我需要向用户显示一个对话框以重试上次失败的操作。如果用户选择重试,程序应该重试该任务,否则程序的执行应该在记录异常后继续。任何人都对如何实现此功能有一个高层次的想法?

最佳答案

2017 年 5 月更新

C# 6 异常过滤器使 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);
}

原创

编写重试函数的方法有很多种:您可以使用递归或任务迭代。有一个 discussion不久前在希腊 .NET 用户组中讨论了执行此操作的不同方法。
如果您使用的是 F#,则还可以使用 Async 结构。不幸的是,您至少不能在 Async CTP 中使用 async/await 结构,因为编译器生成的代码不喜欢多个等待或 catch block 中可能的重新抛出。

递归版本可能是在 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 block 中的 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--;
}
}
}

至于询问用户,你可以修改Retry来调用一个函数询问用户,并通过TaskCompletionSource返回一个任务,当用户回答时触发下一步,eg:

 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);
}

关于c# - 在任务异常的情况下,根据用户输入多次重试任务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10490307/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com