我正在使用一些使用Mono.Mac(3.2.3)的REST请求与服务器通信,作为重试机制,我正在悄悄地试图在HTTP操作失败或超时的情况下多次尝试。
我有以下内容;
var tries = 0;
while (tries <= ALLOWED_TRIES)
{
try
{
postTask.Start();
tries++;
if (!postTask.Wait(Timeout))
{
throw new TimeoutException("Operation timed out");
}
break;
} catch (Exception e) {
if (tries > ALLOWED_TRIES)
{
throw new Exception("Failed to access Resource.", e);
}
}
}
任务使用父方法的参数,如此;
var postTask = new Task<HttpWebResponse>(() => {return someStuff(foo, bar);},
Task.Factory.CancellationToken,
Task.Factory.CreationOptions);
问题似乎是任务不希望在postTask.Start()
首次完成(以及后续失败)后再次运行。有没有一种简单的方法可以做到这一点,还是我以这种方式滥用任务?是否有某种方法可以将任务重置为初始状态,或者我最好使用某种工厂?
答案 0 :(得分:4)
由于以下几个原因,你确实在滥用Task
:
您不能多次运行同一任务。完成后,就完成了。
建议不要手动构建Task
对象,其中Task.Run
和Task.Factory.Start
。
您不应将Task.Run
/ Task.Factory.Start
用于执行IO绑定工作的任务。它们用于CPU绑定工作,因为它们从ThreadPool
“借用”一个线程来执行任务操作。相反,为此使用基于纯异步Task
的API,不需要专门的线程来完成。
例如,下面您可以从UI线程调用GetResponseWithRetryAsync
并仍然保持UI响应:
async Task<HttpWebResponse> GetResponseWithRetryAsync(string url, int retries)
{
if (retries < 0)
throw new ArgumentOutOfRangeException();
var request = WebRequest.Create(url);
while (true)
{
try
{
var result = await request.GetResponseAsync();
return (HttpWebResponse)result;
}
catch (Exception ex)
{
if (--retries == 0)
throw; // rethrow last error
// otherwise, log the error and retry
Debug.Print("Retrying after error: " + ex.Message);
}
}
}
更多阅读:
答案 1 :(得分:0)
我建议做这样的事情:
private int retryCount = 3;
...
public async Task OperationWithBasicRetryAsync()
{
int currentRetry = 0;
for (; ;)
{
try
{
// Calling external service.
await TransientOperationAsync();
// Return or break.
break;
}
catch (Exception ex)
{
Trace.TraceError("Operation Exception");
currentRetry++;
// Check if the exception thrown was a transient exception
// based on the logic in the error detection strategy.
// Determine whether to retry the operation, as well as how
// long to wait, based on the retry strategy.
if (currentRetry > this.retryCount || !IsTransient(ex))
{
// If this is not a transient error
// or we should not retry re-throw the exception.
throw;
}
}
// Wait to retry the operation.
// Consider calculating an exponential delay here and
// using a strategy best suited for the operation and fault.
Await.Task.Delay();
}
}
// Async method that wraps a call to a remote service (details not shown).
private async Task TransientOperationAsync()
{
...
}
此代码来自Microsoft的重试模式设计。您可以在此处查看:https://msdn.microsoft.com/en-us/library/dn589788.aspx