Restarting a task in the background if certain errors occur

I use some REST requests using Mono.Mac (3.2.3) to communicate with the server, and as a retry mechanism, I calmly try to give several attempts an HTTP action, if they fail, or a timeout.

I have the following:

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

If the task uses the parameters of the parent method, for example:

 var postTask = new Task<HttpWebResponse>(() => {return someStuff(foo, bar);}, Task.Factory.CancellationToken, Task.Factory.CreationOptions); 

The problem is that after the first completion (and subsequent failure), the task does not want to start again with postTask.Start() . Is there an easy way to do this, or am I misusing tasks in this way? Is there any method that resets the task to its original state, or is it better for me to use some kind of factory?

+3
source share
2 answers

You really abuse Task here, for several reasons:

  • You cannot run the same task more than once. When this is done, it will be done.

  • It is not recommended to create a Task object manually, there is Task.Run and Task.Factory.Start for this.

  • You should not use Task.Run / Task.Factory.Start for a task that works with IO binding. They are designed to work with the processor, since they "occupy" the thread from ThreadPool to complete the task. Instead, use pure async Task APIs that do not require a dedicated thread.

For example, below you can call GetResponseWithRetryAsync from the user interface thread and still support the user interface:

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

Additional Information:

"Task.Factory.StartNew" against a new task (...). Get started . "

Task.Run vs Task.Factory.StartNew .

+3
source

I would recommend doing something like this:

 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() { ... } 

This code is from Microsoft's Retry Pattern Design template. You can check it here: https://msdn.microsoft.com/en-us/library/dn589788.aspx

0
source

All Articles