在超时期限后取消 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;
}