在C#中同步运行带有返回值的异步方法的问题

时间:2014-03-07 09:56:24

标签: c# asynchronous task-parallel-library async-await

我正在尝试使用async / await功能在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);
    });
}

表单thisthat答案,我怀疑可以调用runTask.Result以等待异步方法完成然后检索结果。但是,当我运行代码时,对ReferenceRun的调用永远不会返回。当我在调试器中暂停执行时,我可以看到它挂起在return runTask.Result语句。

这里发生了什么?

修改 根据{{​​3}},请求的行为可以这样实现:

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

2 个答案:

答案 0 :(得分:3)

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方法将阻止,直到该调用完成
  • 该方法的其余部分只是卸载到另一个线程,因此除了避免阻止调用者之外没有其他好处

在这种情况下,最好只公开一个纯粹的同步方法,并让调用者在必要时将它卸载到另一个线程。

答案 1 :(得分:0)

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

试试这个。您需要先启动任务才能使用结果。