問題描述
我的應用程序中的所有服務調用都是作為任務實現的.當某個任務出現故障時,我需要向用戶顯示一個對話框來重試上次失敗的操作.如果用戶選擇重試,程序應該重試該任務,否則程序的執行應該在記錄異常后繼續.任何人對如何實現這個功能有一個高層次的想法?
All the service calls in my application are implemented as tasks.When ever a task is faulted ,I need to present the user with a dialog box to retry the last operation failed.If the user chooses retry the program should retry the task ,else the execution of the program should continue after logging the exception.Any one has got a high level idea on how to implement this functionality ?
推薦答案
UPDATE 5/2017
C# 6 異常過濾器使 catch
子句更加簡單:
C# 6 exception filters make the catch
clause a lot simpler :
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);
}
原創
編寫重試函數的方法有很多種:您可以使用遞歸或任務迭代.不久前,希臘 .NET 用戶組中有一個 討論做到這一點的不同方法.
如果您使用 F#,您還可以使用 Async 構造.不幸的是,您至少不能在 Async CTP 中使用 async/await 構造,因為編譯器生成的代碼不喜歡多個等待或可能在 catch 塊中重新拋出.
There are many ways to code a Retry function: you can use recursion or task iteration. There was a discussion in the Greek .NET User group a while back on the different ways to do exactly this.
If you are using F# you can also use Async constructs. Unfortunately, you can't use the async/await constructs at least in the Async CTP, because the code generated by the compiler doesn't like multiple awaits or possible rethrows in catch blocks.
遞歸版本可能是在 C# 中構建重試的最簡單方法.以下版本不使用 Unwrap 并在重試之前添加可選延遲:
The recursive version is perhaps the simplest way to build a Retry in C#. The following version doesn't use Unwrap and adds an optional delay before retries :
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.
The StartNewDelayed function comes from the ParallelExtensionsExtras samples and uses a timer to trigger a TaskCompletionSource when the timeout occurs.
F# 版本要簡單得多:
The F# version is a lot simpler:
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
不幸的是,在 C# 中使用 Async CTP 中的 async/await 編寫類似的東西是不可能的,因為編譯器不喜歡 catch 塊中的 await 語句.以下嘗試也靜默失敗,因為運行時不喜歡在異常后遇到等待:
Unfortunatley, it isn't possible to write something similar in C# using async/await from the Async CTP because the compiler doesn't like await statements inside a catch block. The following attempt also fails silenty, because the runtime doesn't like encountering an await after an exception:
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返回任務的函數,在用戶回答時觸發下一步,例如:
As for asking the user, you can modify Retry to call a function that asks the user and returns a task through a TaskCompletionSource to trigger the next step when the user answers, eg:
private static Task<bool> AskUser()
{
var tcs = new TaskCompletionSource<bool>();
Task.Factory.StartNew(() =>
{
Console.WriteLine(@"Error Occured, continue? YN");
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 如此受歡迎.
With all the continuations, you can see why an async version of Retry is so desirable.
更新:
在 Visual Studio 2012 Beta 中,以下兩個版本有效:
In Visual Studio 2012 Beta the following two versions work:
帶有while循環的版本:
A version with a while loop:
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);
}
這篇關于任務發生異常時,根據用戶輸入多次重試任務的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!