Understanding async/await vs Wait in C# with “ContinueWith” behavior

泄露秘密 提交于 2021-02-18 10:46:06

问题


One method is a standard async method, like this one :

private static async Task AutoRetryHandlerAsync_Worker(Func<Task<bool>> taskToRun,...)

I have tested two implementations, one that use await and the other uses .Wait()

The two implementations are not equal at all because the same tests are failing with the await version but not the Wait() one.

The goal of this method is to "execute a Task returned by the input function, and retry by executing the same function until it works" (with limitations to stop automatically if a certain number of tries is reached).

This works:

private static async Task AutoRetryHandlerAsync_Worker(Func<Task<bool>> taskToRun,...)
{
    try {
       await taskToRun();
    }
    catch(Exception) 
   {
       // Execute later, and wait the result to complete
       await Task.Delay(currentDelayMs).ContinueWith(t =>
       {
            // Wait for the recursive call to complete
            AutoRetryHandlerAsync_Worker(taskToRun).Wait();
       });

       // Stop
       return;
    }    
}

And this (with async t => and the usage of await instead of t => and the usage of .Wait() doesn't work at all because the result of the recursive call is not awaited before the final return; is executed :

private static async Task AutoRetryHandlerAsync_Worker(Func<Task<bool>> taskToRun,...)
{
    try {
       await taskToRun();
    }
    catch(Exception) 
   {
       // Execute later, and wait the result to complete
       await Task.Delay(currentDelayMs).ContinueWith(async t =>
       {
            // Wait for the recursive call to complete
            await AutoRetryHandlerAsync_Worker(taskToRun);
       });

       // Stop
       return;
    }    
}

I'm trying to understand why this simple change does change everything, when it's supposed to do the exact same thing : waiting the ContinueWith completion.

If I extract the task ran by the ContinueWith method, I do see the state of the ContinueWith function passing to "ranToCompletion" before the return of the inner await completes.

Why? Isn't it supposed to be awaited?


Concrete testable behaviour

public static void Main(string[] args)
{
    long o = 0;
    Task.Run(async () =>
    {
        // #1 await
        await Task.Delay(1000).ContinueWith(async t =>
        {
            // #2 await
            await Task.Delay(1000).ContinueWith(t2 => {
                o = 10;
            });
        });
        var hello = o;
    });


    Task.Delay(10000).Wait();
}

Why does var hello = o; is reached before o=10?

Isn't the #1 await supposed to hang on before the execution can continue?


回答1:


The lambda syntax obscures the fact that you ContinueWith(async void ...).

async void methods are not awaited and any errors they throw will go unobserved.

And to your base question, retrying from within a catch is not a recommended practice anyway. Too much going on, catch blocks should be simple. And bluntly retrying for all exception types is also very suspect. You ought to have an idea what errors could warrant a retry, and let the rest pass.

Go for simplicity and readability:

while (count++ < N)
{
   try
   {          
      MainAction();
      break;      
   }
   catch(MoreSpecificException ex) { /* Log or Ignore */ }

   Delay();
}


来源:https://stackoverflow.com/questions/43542654/understanding-async-await-vs-wait-in-c-sharp-with-continuewith-behavior

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!