在超时期限后取消 LoadAsync 操作并随后调用 LoadAsync 会引发异常
Cancelling a LoadAsync operation after a timeout period and recalling LoadAsync afterwards throws exception
我在 C++/winrt 上使用 SerialDevice,需要侦听来自端口的数据。当数据通过端口流式传输时,我可以成功地使用 SerialDevice,但是如果没有读取任何内容,即使我通过 SerialDevice.ReadTimeout() 和 SerialDevice.WriteTimeout() 设置超时,DataReader.LoadAsync() 函数也会挂起。因此,要取消操作,我正在使用 IAsyncOperation 的 wait_for() 操作,该操作在提供的时间间隔后超时,我调用 IAsyncOperation 的 Cancel() 和 Close()。问题是我无法再调用 DataReader.LoadAsync() 而不得到 take_ownership_from_abi 异常。如何正确取消 DataReader.LoadAsync() 调用以允许对同一对象的后续调用 LoadAsync()?
为了解决这个问题,我尝试设置 SerialDevice 的超时,但这并不影响 DataRead.LoadAsync() 调用。我还尝试将 create_task 与取消令牌一起使用,这也不允许对 LoadAsync() 进行额外调用。经过大量搜索才找到 Kenny Kerr 的这篇文章:
https://kennykerr.ca/2019/06/10/cppwinrt-async-timeouts-made-easy/
他在其中描述了 IAsyncOperation 的 wait_for 函数的使用。
这里是SerialDevice和DataReader的初始化:
DeviceInformation deviceInfo = devices.GetAt(0);
m_serialDevice = SerialDevice::FromIdAsync(deviceInfo.Id()).get();
m_serialDevice.BaudRate(nBaudRate);
m_serialDevice.DataBits(8);
m_serialDevice.StopBits(SerialStopBitCount::One);
m_serialDevice.Parity(SerialParity::None);
m_serialDevice.ReadTimeout(m_ts);
m_serialDevice.WriteTimeout(m_ts);
m_dataWriter = DataWriter(m_serialDevice.OutputStream());
m_dataReader = DataReader(m_serialDevice.InputStream());
这是 LoadAsync 调用:
AsyncStatus iainfo;
auto async = m_dataReader.LoadAsync(STREAM_SIZE);
try {
auto iainfo = async.wait_for(m_ts);
}
catch (...) {};
if (iainfo != AsyncStatus::Completed)
{
async.Cancel();
async.Close();
return 0;
}
else
{
nBytesRead = async.get();
async.Close();
}
因此,在 AsyncStatus 未完成的情况下,将调用 IAsyncOperation Cancel() 和 Close(),根据文档,这应该取消异步调用,但现在在后续的 LoadAsync 调用中,我得到一个 take_ownership_from_abi 例外。
任何人都知道我做错了什么?为什么 SerialDevice 超时首先不起作用?有没有更好的方法来取消允许进一步调用而无需重新初始化 DataReader 的异步调用?总的来说,感觉C++/winrt space里面的activity很少,而且文档也严重缺乏(试了大概一天其他的才找到wait_for方法东西并通过不同的帖子随机搜索线索) - 我是否遗漏了什么或者真的是这样吗?
谢谢!
原因:等待时间结束后,异步对象在AsyncStatus::Started state
。这意味着异步对象仍然是运行。
解决方法:当你使用close()方法时,你可以使用Sleep(m_nTO ) 让异步操作有足够的时间关闭。参考以下代码。
if (iainfo != AsyncStatus::Completed)
{
m_nReadCount++;
//Sleep(m_nTO);
async.Cancel();
async.Close();
Sleep(m_nTO);
return 0;
}
我在 C++/winrt 上使用 SerialDevice,需要侦听来自端口的数据。当数据通过端口流式传输时,我可以成功地使用 SerialDevice,但是如果没有读取任何内容,即使我通过 SerialDevice.ReadTimeout() 和 SerialDevice.WriteTimeout() 设置超时,DataReader.LoadAsync() 函数也会挂起。因此,要取消操作,我正在使用 IAsyncOperation 的 wait_for() 操作,该操作在提供的时间间隔后超时,我调用 IAsyncOperation 的 Cancel() 和 Close()。问题是我无法再调用 DataReader.LoadAsync() 而不得到 take_ownership_from_abi 异常。如何正确取消 DataReader.LoadAsync() 调用以允许对同一对象的后续调用 LoadAsync()?
为了解决这个问题,我尝试设置 SerialDevice 的超时,但这并不影响 DataRead.LoadAsync() 调用。我还尝试将 create_task 与取消令牌一起使用,这也不允许对 LoadAsync() 进行额外调用。经过大量搜索才找到 Kenny Kerr 的这篇文章: https://kennykerr.ca/2019/06/10/cppwinrt-async-timeouts-made-easy/ 他在其中描述了 IAsyncOperation 的 wait_for 函数的使用。
这里是SerialDevice和DataReader的初始化:
DeviceInformation deviceInfo = devices.GetAt(0);
m_serialDevice = SerialDevice::FromIdAsync(deviceInfo.Id()).get();
m_serialDevice.BaudRate(nBaudRate);
m_serialDevice.DataBits(8);
m_serialDevice.StopBits(SerialStopBitCount::One);
m_serialDevice.Parity(SerialParity::None);
m_serialDevice.ReadTimeout(m_ts);
m_serialDevice.WriteTimeout(m_ts);
m_dataWriter = DataWriter(m_serialDevice.OutputStream());
m_dataReader = DataReader(m_serialDevice.InputStream());
这是 LoadAsync 调用:
AsyncStatus iainfo;
auto async = m_dataReader.LoadAsync(STREAM_SIZE);
try {
auto iainfo = async.wait_for(m_ts);
}
catch (...) {};
if (iainfo != AsyncStatus::Completed)
{
async.Cancel();
async.Close();
return 0;
}
else
{
nBytesRead = async.get();
async.Close();
}
因此,在 AsyncStatus 未完成的情况下,将调用 IAsyncOperation Cancel() 和 Close(),根据文档,这应该取消异步调用,但现在在后续的 LoadAsync 调用中,我得到一个 take_ownership_from_abi 例外。 任何人都知道我做错了什么?为什么 SerialDevice 超时首先不起作用?有没有更好的方法来取消允许进一步调用而无需重新初始化 DataReader 的异步调用?总的来说,感觉C++/winrt space里面的activity很少,而且文档也严重缺乏(试了大概一天其他的才找到wait_for方法东西并通过不同的帖子随机搜索线索) - 我是否遗漏了什么或者真的是这样吗? 谢谢!
原因:等待时间结束后,异步对象在AsyncStatus::Started state
。这意味着异步对象仍然是运行。
解决方法:当你使用close()方法时,你可以使用Sleep(m_nTO ) 让异步操作有足够的时间关闭。参考以下代码。
if (iainfo != AsyncStatus::Completed)
{
m_nReadCount++;
//Sleep(m_nTO);
async.Cancel();
async.Close();
Sleep(m_nTO);
return 0;
}