12

I'm having an issue where a task is completing, but not returning back. I have a website and a web service on different servers. The website makes a call to the web service which utilizes a library with function myFunction(). If I make a call to myFunction from a console app on the web service's server, it will return as expected. However, when I make the call from the website to the web service to call myFunction(), it will get to "Step 3", but not "Step 4". I have a simplified version of the calls below.

private string myFunction()
{
    string myStr = myDoWork().GetAwaiter().GetResult();

    return myStr;
}

private async Task<string> myDoWork()
{
    logger.Debug("Step 1");
    string answer = await aFunction();

    logger.Debug("Step 4");

    return answer;
}

public async Task<string> aFunction()
{
    logger.Debug("Step 2");
    return await bFunction(CancellationToken.None);
}

AsyncLock myLock = new AsyncLock();
public Task<string> bFunction(CancellationToken cToken)
{
    return Task.Run(
        async () =>
        {
            using (await myLock(cToken))
            {
                logger.Debug("Step 3");
                result = "Hello";
                return result;
            }
        },
        cToken);
}

I'm new to async and await, so any help would be appreciated.

Jesse McConahie
  • 316
  • 1
  • 3
  • 12
  • If possible, `myFunction()` should also be async. Weird things can happen if you block on an async call. Also, why the `Task.Run` business in `bFunction`? – Nate Barbettini May 13 '16 at 19:56
  • there's no reason why `Step 4` wouldn't be executed here – Jonesopolis May 13 '16 at 20:01
  • @NateBarbettini I don't know why the Task.Run stuff. I tried to simplify the code as much as possible, but you did bring my attention to a piece that might matter. I'm reusing code that was written to be asynchronous for a synchronous task. Rewriting it all without the async stuff would be quite an undertaking. – Jesse McConahie May 13 '16 at 20:05
  • In general, it's a bad idea to mix sync+async. The ideal solution is to make it either all asynchronous, or all synchronous. I don't see any other issues with your sample, other than you could get rid of `Task.Run(async () =>{ })` to simplify that part a lot. – Nate Barbettini May 13 '16 at 20:06
  • 2
    Most probably you are having a deadlock because you are mixing synchronous and asynchronous code. `.GetAwaiter().GetResult()` is synchronously waiting for the task to complete. See http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html for more information – Yacoub Massad May 13 '16 at 20:52

1 Answers1

12

It's almost certainly a deadlock in myFunction(). Step 4 is scheduled to execute on the main thread, but it can't because the main thread is blocked waiting on GetResult().

It should be something closer to this:

private string myFunction()
{
    // This is NOT good style - you should avoid blocking on the result of a task.
    string myStr = Task.Run(async () => await myDoWork()).GetAwaiter().GetResult();
    return myStr;
}

private async Task<string> myDoWork()
{
    logger.Debug("Step 1");
    string answer = await aFunction();
    logger.Debug("Step 4");
    return answer;
}

public Task<string> aFunction()
{
    logger.Debug("Step 2");
    return bFunction(CancellationToken.None);
}

AsyncLock myLock = new AsyncLock();
public async Task<string> bFunction(CancellationToken cToken)
{
    using (await myLock(cToken))
    {
        logger.Debug("Step 3");
        return "Hello";
    }
}

In general, Task.Run() should be called from the highest level possible. Keep things as synchronous as possible and let the caller decide if they want to use a background thread.

piedar
  • 2,599
  • 1
  • 25
  • 37
  • Thank you so much! This fixed it. – Jesse McConahie May 13 '16 at 21:23
  • 3
    @JesseMcConahie the better solution is fix your code to make `myFunction` async and not use `Task.Run` nor `.GetResult()` – Scott Chamberlain May 13 '16 at 21:24
  • @ScottChamberlain I agree. – Jesse McConahie May 13 '16 at 21:33
  • Today I too ran into same problem, though above solution worked for me, later i found that using ConfigureAwait(false) like `await _client.PostAsJsonAsync(apiPath, content).ConfigureAwait(false);` too solved problem.. @scottChamberlain is it okay to use ConfigureAwait(false) while making an async call? newb to task programming, appreciate if anyone could provide your input here. – mabiyan Aug 02 '18 at 14:10
  • @Mabiyan It's ok to use `ConfigureAwait(false)` as long as [the rest of the function is safe to run in the background](https://stackoverflow.com/a/27851460/1925996). The number one place where it's __not__ safe is when updating the UI after `await`. – piedar Aug 02 '18 at 15:47