C# 如果任务中出现异常,请根据用户输入重试任务多次
我的应用程序中的所有服务调用都作为任务实现。当任务出现故障时,我需要向用户提供一个对话框,以重试上次失败的操作。如果用户选择重试,程序应重试该任务,否则,在记录异常后,程序应继续执行。任何人都对如何实现此功能有较高的认识?在较高的层次上,我发现根据您拥有的和想要的内容制作函数签名很有帮助 你有:C# 如果任务中出现异常,请根据用户输入重试任务多次,c#,wpf,exception-handling,task-parallel-library,task,C#,Wpf,Exception Handling,Task Parallel Library,Task,我的应用程序中的所有服务调用都作为任务实现。当任务出现故障时,我需要向用户提供一个对话框,以重试上次失败的操作。如果用户选择重试,程序应重试该任务,否则,在记录异常后,程序应继续执行。任何人都对如何实现此功能有较高的认识?在较高的层次上,我发现根据您拥有的和想要的内容制作函数签名很有帮助 你有: 为您提供任务的函数(Func)。我们将使用该函数,因为任务本身通常不可重试 确定整个任务是否已完成或应重试的函数(Func) 你想要: 总体任务 因此,您将拥有如下函数: Task Retry(
- 为您提供任务的函数(
)。我们将使用该函数,因为任务本身通常不可重试Func
- 确定整个任务是否已完成或应重试的函数(
)Func
- 总体任务
Task Retry(Func<Task> action, Func<Task, bool> shouldRetry);
这里明显的问题是,只会发生1次重试。为了避免这种情况,您需要为函数自身调用提供一种方法。使用lambdas执行此操作的通常方法如下:
//error checking
var result = new TaskCompletionSource<object>();
Func<Task, Task> retryRec = null; //declare, then assign
retryRec = (t) => { if (shouldRetry(t))
return action().ContinueWith(retryRec).Unwrap();
else
{
if (t.IsFaulted)
result.TrySetException(t.Exception);
//and so on
return result.Task; //need to return something
}
};
action().ContinueWith(retryRec);
return result.Task;
//错误检查
var result=new TaskCompletionSource();
Func retryRec=null//声明,然后分配
retryRec=(t)=>{if(shouldRetry(t))
返回操作().ContinueWith(retryRec.Unwrap();
其他的
{
如果(t.IsFaulted)
结果:trysetexeception(t.Exception);
//等等
return result.Task;//需要返回一些内容
}
};
action().ContinueWith(retryRec);
返回结果。任务;
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静态异步任务重试(Func-Func,int-retryCount)
{
while(true)
{
尝试
{
var result=wait Task.Run(func);
返回结果;
}
当(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);
}
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);
}
private静态异步任务重试(Func-Func,int-retryCount)
{
尝试
{
var result=wait Task.Run(func);
返回结果;
}
当(retryCount-->0){}时捕获
返回等待重试(func,retryCount);
}
原创
编写重试函数的方法有很多:可以使用递归或任务迭代。希腊.NET用户组a中有一个用户组,他们使用不同的方法来实现这一点。如果使用F#,还可以使用异步构造。不幸的是,至少在异步CTP中不能使用async/await构造,因为编译器生成的代码不喜欢catch块中的多次等待或可能的重试 递归版本可能是在C#中构建重试的最简单方法。以下版本不使用“展开”,并在重试前添加可选延迟:
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;
}
private静态任务重试(Func-Func,int-retryCount,int-delay,TaskCompletionSource tcs=null)
{
如果(tcs==null)
tcs=新任务完成源();
Task.Factory.StartNew(func).ContinueWith(\u original=>
{
如果(_original.IsFaulted)
{
如果(retryCount==0)
SetException(_original.Exception.innerException);
其他的
Task.Factory.StartNewDelayed(延迟)。ContinueWith(t=>
{
重试(func,retryCount-1,delay,tcs);
});
}
其他的
tcs.SetResult(_原始结果);
});
返回tcs.Task;
}
该函数来自示例,并在超时发生时使用计时器触发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
让我们重试(异步计算:异步=
让rec重试'retryCount=
异步的{
尝试
让!result=异步计算
返回结果
使用exn->
如果retryCount=0,则
返回上升exn
其他的
return!retry'(retryCount-1)
}
重试“retryCount”
不幸的是,不可能使用异步CTP中的async/await在C#中编写类似的代码,因为编译器不喜欢catch块中的await语句。以下尝试也会失败,因为运行时不喜欢在异常后遇到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--;
}
}
}
private静态异步任务重试(Func-Func,int-retryCount)
{
while(true)
{
尝试
{
var result=wait TaskEx.Run(func);
返回结果;
}
抓住
{
如果(retryCount==0)
投掷;
重新计数--;
}
}
}
至于询问用户,您可以修改Retry以调用一个函数,该函数询问用户并通过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;
}
private静态任务AskUser()
{
var tcs=new TaskCompletionSource();
Task.Factory.StartNew(()=>
{
Console.WriteLine(@“发生错误,是否继续?Y\N”);
var response=Console.ReadKey();
SetResult(response.KeyChar=='y');
});
返回tcs.Task;
}
私有静态任务RetryAsk(Func Func,int retryCount,TaskCompletionSource tcs=null)
{
如果(tcs==null)
tcs=新任务完成源();
Task.Factory.StartNew(func).ContinueWith(\u original=>
{
如果(_original.IsFaulted)
{
如果(retryCount==0)
tcs.S
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);
}
static Task<T> RetryWhile<T>(
Func<int, Task<T>> func,
Func<Exception, int, bool> shouldRetry )
{
return RetryWhile<T>( func, shouldRetry, new TaskCompletionSource<T>(), 0, Enumerable.Empty<Exception>() );
}
static Task<T> RetryWhile<T>(
Func<int, Task<T>> func,
Func<Exception, int, bool> shouldRetry,
TaskCompletionSource<T> tcs,
int previousAttempts, IEnumerable<Exception> previousExceptions )
{
func( previousAttempts ).ContinueWith( antecedent =>
{
if ( antecedent.IsFaulted )
{
var antecedentException = antecedent.Exception;
var allSoFar = previousExceptions
.Concat( antecedentException.Flatten().InnerExceptions );
if ( shouldRetry( antecedentException, previousAttempts ) )
RetryWhile( func,shouldRetry,previousAttempts+1, tcs, allSoFar);
else
tcs.SetException( allLoggedExceptions );
}
else
tcs.SetResult( antecedent.Result );
}, TaskContinuationOptions.ExecuteSynchronously );
return tcs.Task;
}