0
votes

I have the following async method, which is a simpler wait and retry in case of failures:

public async Task RetryAsync(Func<Task> _action, int _ms = 1000, int _counter = 3) {
    while (true) {
        try {
            await _action();
            return; // success!
        }
        catch {
            if (--_counter == 0)
                throw;
            await Task.Delay(_ms);
        }
    }
}

To, in theory, be called like this:

await RetryAsync(async () => {
    _newID = myDBFunction();
}, 300);

Since the function passed to the RetryAsync method does not contain any await, then obviously this gets the warning:

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

... and changing the calling code to this fixes the problem:

await RetryAsync(async () => {
    await Task.Run(() => _newID = myDBFunction(););
}, 300);

Is there any other way to achieve parallelism for this simple case besides using Task.Run()? Any disadvantage you see to the latest code with Task.Run() on it?

1
Why do you need to pass async method, if in reality it's not async?Ofir Winegarten
Because in most cases it will be an async method what it will be passed, plus I do want to achieve parallelism.Miguel Mateo
That's fine, you have your Func<Task> signature, but the implementation, doesn't have to be async. You can as you said, just use Task.Run without the await and async - What's wrong with that?Ofir Winegarten
@OfirWinegarten, please write how would you make the call OR how would you change the Retry function.Miguel Mateo
I would not change the RetryAsync, Just remove the async and await. await RetryAsync(() => Task.Run(() => _newID = myDBFunction()), 300);Ofir Winegarten

1 Answers

2
votes

First, I recommend using Polly. It's widely used, extensively tested, and has native support for asynchronous as well as synchronous usage.

But if you want to keep using your own, you can add a synchronous equivalent:

public async Task RetryAsync(Func<Task> _action, int _ms = 1000, int _counter = 3);
public void Retry(Action _action, int _ms = 1000, int _counter = 3);

which can be called as such:

Retry(() => {
  _newID = myDBFunction();
}, 300);

If you want to always put synchronous code on a thread pool, you can add an overload for that, to:

public async Task RetryAsync(Func<Task> _action, int _ms = 1000, int _counter = 3);
public async Task RetryAsync(Action _action, int _ms = 1000, int _counter = 3) =>
    await RetryAsync(() => Task.Run(_action), _ms, _counter);

which can be called as such:

await RetryAsync(() => {
    _newID = myDBFunction();
}, 300);