0

When I run this code everything works fine:

public async void InvokePlugin(MyObject xTask)
{
    try
    {
        var hndlr = new TimeoutHandler(RunTask);
        var asyncResult = hndlr.BeginInvoke(xTask, null, new object());
        if (!asyncResult.AsyncWaitHandle.WaitOne(xTask.Timeout, false))
        {
            throw new TimeoutException("Plugin didn't complete processing in a timely manner.");
        }
        hndlr.EndInvoke(asyncResult);
    }
    catch (Exception ex)
    {
        //Handle Exceptions
    }
}

private delegate void TimeoutHandler(MyObject xTask);

I want to update this code to use Async/Await. I tried doing it like this:

public async void InvokePlugin(MyObject xTask)
{
    try
    {
        var runTask = Task.Run(() => { RunTask(xTask); });
        if (await Task.WhenAny(runTask, Task.Delay(xTask.Timeout)) == runTask)
        {
            // Task completed within timeout.
            // Consider that the task may have faulted or been canceled.
            // We re-await the task so that any exceptions/cancellation is rethrown.
            await runTask;
        }
        else
        {
            throw new TimeoutException("Plugin didn't complete processing in a timely manner.");
        }
    }
    catch (Exception ex)
    {
        //Handle Exceptions
    }
} 

...but it's not working. Clearly I'm doing somethign wring. It does call the RunTask Method and executes the first 2 lines fine but then it just ends and I can't seem to catch the exception in either the TaskRun method or code above. All I see in the Output windows is "Program has exited with code 0 (0x0)."

If the experts out there can either point me to what I'm doing wrong or give me suggestions as to how I can catch the exception and handle it I would be very grateful.

Also if you feel I missed any important details please ask and I will update my question.

Usually I'd say if it works don't fix it but in this case I'm trying to rearchitect a bit to allow for some enhancements so here I am.

LorneCash
  • 1,446
  • 2
  • 16
  • 30
  • 3
    First thing I noticed, avoid signatures with `async void`. Use `async Task` instead so there is something to await for the caller. The only exception are event handlers in WPF and WinForms apps. – Igor Mar 29 '17 at 19:09
  • 6
    `await` is not the same as `Wait`. your original code is synchronous (`WaitOne`), so it's not eligible for converting to `async/await`. – Ivan Stoev Mar 29 '17 at 19:11
  • 3
    1) Change `async void` to `async Task`. 2) `await` the task, allowing `async` to grow. 3) In your `Main` method, call `GetAwaiter().GetResult()` on the "top" task. – Stephen Cleary Mar 29 '17 at 21:06
  • @StephenCleary Please post your comment as the solution and I will mark it. – LorneCash Mar 29 '17 at 21:18

1 Answers1

3
  1. Change async void to async Task. See my article on async best practices for more information.

After you do this, consume it asynchronously:

  1. await the task, allowing async to grow.

async and await will naturally grow upward through your code base, until they reach Main, which cannot be async.

  1. In your Main method, call GetAwaiter().GetResult() on the "top" task.

Blocking on asynchronous code is generally not a good idea, but blocking on a single task in a Console app's Main method is an exception to that rule.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Why `.GetAwaiter().GetResult()` instead of `.Wait()` or `.Result` ? Even though there may be *some* cases where it's more convenient, it appears in a *lot* of SO questions – Panagiotis Kanavos Mar 30 '17 at 15:25
  • Because `Wait` and `Result` wrap exceptions in an `AggregateException`, complicating error handling code. `GetAwaiter().GetResult()` does not do that unnecessary wrapping. – Stephen Cleary Mar 30 '17 at 15:37
  • The problem is that this has started appearing in a lot of unnecesary places, complicating the code. Instead of being a single call at the end of a console application, it started appearing in web applications, eg [this](http://stackoverflow.com/questions/43027847/code-never-get-past-call-to-eventprocessorhost-registereventprocessorasynclist), [this](http://stackoverflow.com/questions/42947585/getasync-azure-call-no-result/42949748#42949748) and [this](http://stackoverflow.com/questions/42883020/get-token-from-oauth2-middleware) – Panagiotis Kanavos Mar 30 '17 at 15:58
  • Looking at the newest [posts that contain GetAwaiter()](http://stackoverflow.com/search?tab=newest&q=getawaiter()) you'll see that `GetAwaiter()` is getting heavily misused – Panagiotis Kanavos Mar 30 '17 at 16:03
  • Yes, it's misused just like `Wait` and `Result` are. – Stephen Cleary Mar 30 '17 at 16:49