我正在尝试使用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);
});
}
表单this和that答案,我怀疑可以调用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;
}
答案 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;}
试试这个。您需要先启动任务才能使用结果。