0

I'm trying to write a hardware library in C# using the async / await features. For many operations, there will be two methods, one that runs asynchronously, and another one running the asynchronous method synchronously. For example, consider this:

public override bool ReferenceRun(bool positiveDirection) {
    var runTask = ReferenceRunAsync(positiveDirection);
    return runTask.Result;
}

public override async Task<bool> ReferenceRunAsync(bool positiveDirection) {
    String cmd = createAxisCommand(positiveDirection ? refRunPlusCmd : refRunMinusCmd);
    bool writeOK = writeToCOMPort(cmd);
    if ( !writeOK ) return false;
    return await Task.Factory.StartNew(() => {
        WaitForHalt();
        return setParameter(postitionParam, 0);
    });
}

Form this and that answers, I suspected that it was ok to call runTask.Result in order to wait for the async method to complete and then retrieve the result. However, when I run the code, the call to ReferenceRun does never return. When I pause execution in the debugger, I can see that it hangs at the return runTask.Result statement.

What's going on here?

Edit: According to this, the requested behavior can be achieved like this:

public override bool ReferenceRun(bool positiveDirection) {
    var runTask = Task<bool>.Run(async () => { return await ReferenceRunAsync(positiveDirection); });
    return runTask.Result;
}
Community
  • 1
  • 1
Matz
  • 583
  • 1
  • 6
  • 21

2 Answers2

3

In ReferenceRunAsync, when you use await, the current synchronization context (typically the UI thread's context) is captured, and the continuation (what follows the await instruction) is run on that synchronization context. But since you are synchronously waiting for the result in ReferenceRun, the thread is already busy and can't execute the continuation, so you have a deadlock. An easy fix is to call ConfigureAwait(false) on the awaited task to avoid capturing the context:

return await Task.Factory.StartNew(() => {
    WaitForHalt();
    return setParameter(postitionParam, 0);
}).ConfigureAwait(false);

Anyway, in general you shouldn't expose a synchronous wrapper for an asynchronous method, or the other way round. See Stephen Toub's explanations:

Another issue with your code is that ReferenceRunAsync isn't really synchronous, because:

  • writeToCOMPort is a blocking IO operation (just a guess from the name), so the ReferenceRunAsync method will block until that call is complete
  • the rest of the method is just offloaded to a different thread, so there is no real benefit other than to avoid blocking the caller

In this case it would probably be better to just expose a purely synchronous method, and let the caller offload it to another thread if necessary.

Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
0
public override bool ReferenceRun(bool positiveDirection) {
var runTask = ReferenceRunAsync(positiveDirection);
runTask.Wait();
return runTask.Result;}

Try this. You need to start the task befor you can use the result.

Alex Witkowski
  • 545
  • 1
  • 6
  • 13
  • I did try this; it didn't work. Thomas Levesque's answer clarifies why. – Matz Mar 07 '14 at 10:15
  • Indeed, but I think you should add the runTask.Wait to validate that the result is set befor returning it in ReferenceRun. Sorry Answer was supposed to be additional. – Alex Witkowski Mar 07 '14 at 10:20
  • Actually yes, but afaik `Task.Result` is already waiting for the task to finish. – Matz Mar 07 '14 at 10:28