What is the best way to implement a Retry Wrapper in C#?

c#, task-parallel-library

Solution

So long as your method signature returns a T, the main thread will have to block until all retries are completed. However, you can reduce CPU by having the thread sleep instead of doing a manual reset event:

Thread.Sleep(retryInterval);

If you are willing to change your API, you can make it so that you don't block the main thread. For example, you could use an async method:

public async Task<T> RepeatAsync<T, TException>(Func<T> work, TimeSpan retryInterval, int maxExecutionCount = 3) where TException : Exception
{
     for (var i = 0; i < maxExecutionCount; ++i)
     {
        try { return work(); }
        catch (TException ex)
        {
            // allow the program to continue in this case
        }
        // this will use a system timer under the hood, so no thread is consumed while
        // waiting
        await Task.Delay(retryInterval);
     }
}

This can be consumed synchronously with:

RepeatAsync<T, TException>(work, retryInterval).Result;

However, you can also start the task and then wait for it later:

var task = RepeatAsync<T, TException>(work, retryInterval);

// do other work here

// later, if you need the result, just do
var result = task.Result;
// or, if the current method is async:
var result = await task;

// alternatively, you could just schedule some code to run asynchronously
// when the task finishes:
task.ContinueWith(t => {
    if (t.IsFaulted) { /* log t.Exception */ }
    else { /* success case */ }
});

Problem

We currently have a naive RetryWrapper which retries a given func upon the occurrence of an exception: ``` public T Repeat<T, TException>(Func<T> work, TimeSpan retryInterval, int maxExecutionCount = 3) where TException : Exception { ... ``` And for the retryInterval we are using the below logic to "wait" before the next attempt. ``` _stopwatch.Start(); while (_stopwatch.Elapsed <= retryInterval) { // do nothing but actuallky it does! lots of CPU usage specially if retryInterval is high } _stopwatch.Reset(); ``` I don't particularly like this logic, also ideally I would prefer the retry logic NOT to happen on the main thread, can you think of a better way? Note: I am happy to consider answers for .Net >= 3.5

Original source

Related problems