我正在尝试使用异步/等待功能在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;}
尝试一下。您需要启动任务,因为您可以使用结果。