在C#中同步运行异步方法的问题



我正在尝试使用异步/等待功能在C#中编写硬件库。对于许多操作,将有两种方法,一种是异步运行的方法,另一种方法同步运行异步方法。例如,考虑以下方式:

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);
    });
}

形成此问题,我怀疑可以致电runTask.Result,以便等待异步方法完成然后检索结果。但是,当我运行代码时,ReferenceRun的调用确实永远不会返回。当我在调试器中暂停执行时,我可以看到它挂在return runTask.Result语句中。

这里发生了什么?

编辑:据此,可以这样实现要求的行为:

public override bool ReferenceRun(bool positiveDirection) {
    var runTask = Task<bool>.Run(async () => { return await ReferenceRunAsync(positiveDirection); });
    return runTask.Result;
}

ReferenceRunAsync中,当您使用await时,当前同步上下文(通常是UI线程的上下文)被捕获,并且在该同步上下文上运行(await指令下面的内容)。但是,由于您正在同步等待ReferenceRun中的结果,因此该线程已经很忙,无法执行延续,因此您有一个僵局。一个简单的修复是在期待的任务上致电ConfigureAwait(false),以避免捕获上下文:

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

无论如何,通常,您不应以异步方法或相反的方式暴露同步包装器。请参阅Stephen Toub的解释:

  • 我应该揭露异步方法的同步包装器吗?
  • 我应该将异步包装器用于同步方法吗?

您的代码另一个问题是ReferenceRunAsync并不是真正的同步,因为:

  • writeToCOMPort是一个阻止IO操作(只有名称猜测),因此ReferenceRunAsync方法将阻止直到该调用完成
  • 该方法的其余部分只是卸载到另一个线程,因此除了避免阻止呼叫者
  • 外,没有其他好处

在这种情况下

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

尝试一下。您需要启动任务,因为您可以使用结果。

最新更新