在ReadAsync

时间:2019-03-12 06:53:13

标签: c# .net serial-port

我需要一些帮助,以从通过RS-232接口连接到设备的串行端口读取数据。我决定使用async / await样式,因为GUI中将不断更新数据,这些数据应保持用户响应。

下面的代码是我当前实现的本质。

private async void MyReadFromSerialPortMethod()
{
    // (this.serialPort is a System.IO.Ports.SerialPort instance.)
    this.serialPort.DiscardInBuffer();
    this.serialPort.DiscardOutBuffer();

    this.serialPort.Write(/* bytes for "please send me some data now", 0, length */);

    byte[] header = new byte[3];
    await this.serialPort.BaseStream.ReadAsync(header, 0, header.Length);

    // do something with the header info first,
    // like check what kind of data is incoming

    byte[] data = new byte[5];
    await this.serialPort.BaseStream.ReadAsync(data, 0, data.Length);

    // do something with the data,
    // like show it to the user eventually
}

我的问题是,在上面的代码中,ReadAsync调用通常仅从输入中读取一个字节,然后从await返回(并非总是 ,但通常)。如果使用的是同步方法,则可以安排ReceivedBytesThreshold包含必需的字节数,但是在执行此操作时不希望保留UI线程。据我所知,ReadAsync没有延迟返回的阈值。这将非常有帮助。

我已经实现了以下解决方案,但对我来说这并不是一个好的解决方案。围绕异步方法循环感觉就像我只是在编写一个忙于等待的循环来等待输入缓冲区填满,我不妨使用同步版本。虽然我知道await可能确实会在到达每个字节之间在一定程度上将控制权返回给调用方,但这不是关于对代码进行速度和效率分析的问题,而是关于正确的编码模式以及我是否正确利用异步编程或对其进行限制。

    byte[] header = new byte[3];
    int bytesRead = 0;
    while (bytesRead < header.Length)
    {
        bytesRead += await this.serialPort.BaseStream.ReadAsync(header, bytesRead, header.Length-bytesRead);
    }
    // similarly for byte[] data...

1 个答案:

答案 0 :(得分:1)

为了获得多字节响应,您仍然需要一个循环,因为SerialPort.ReceivedBytesThreshold属性仅与SerialPort.DataReceived事件相关。但是可能在请求响应方案中,您仍然需要使用同步API和Task.Run(),因为异步API会完全忽略SerialPort超时属性,而几乎会完全忽略CancellationToken。这意味着异步SerialPort操作可以永久挂起,从而保留缓冲区并可能锁定。

此外,您还需要同步对关键部分(与串行端口进行交互的逻辑块)的访问,因此多个请求不会互相干预(使用lock关键字,SemaphoreSlim实例或类似)。

有关示例实现,请查看关于StackOverflow的C# await event and timeout in serial port communication讨论。

通常,您需要执行相关测试,并查看一种方法是否可以带来良好的用户体验。

有关更多信息,请检查: