C# 强制执行a";“字节阈值”;关于ReadAsync

C# 强制执行a";“字节阈值”;关于ReadAsync,c#,.net,serial-port,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(

我需要一些帮助读取数据从一个串行端口连接到一个带有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
调用通常只从输入中读取一个字节,然后从
wait
返回(不总是,但通常)。如果我使用同步方法,我可以安排
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...
byte[]头=新字节[3];
int字节读取=0;
while(bytesRead
为了获得多字节响应,您仍然需要一个循环,因为
SerialPort.ReceivedBytestThreshold
属性仅与
SerialPort.DataReceived
事件相关。但可能在请求-响应场景中,您仍然需要使用同步API和
Task.Run()
,因为异步API完全忽略
SerialPort
超时属性,几乎完全忽略
CancellationToken
。这意味着异步
SerialPort
操作可以永久挂起缓冲区,也可能挂起锁

您还需要同步对关键部分的访问(与串行端口交互的逻辑块),以便多个请求不会相互干扰(使用
lock
关键字、
SemaphoreSlim
实例或类似方法)

例如,请查看有关StackOverflow的讨论

一般来说,您需要执行相关的测试,看看一种方法是否能带来良好的用户体验

有关更多信息,请查看:

  • 金·汉密尔顿的文章
  • 关于CoreFX GitHub的问题
  • Stephen Toub的文章
  • 关于堆栈溢出的讨论
  • Microsoft文档页面

您的代码非常完美,它不会占用CPU时间,而且非常高效。如果你想取消操作,如果用户请求取消,请使用<代码>取消标记> /代码>并抛出。put
token.throwifcancellationrequest()另一种解决方案是使用
任务。运行
在另一个线程上运行同步代码。您可以在
基流
上同时运行一个单独的线程来执行
读取
写入
。或者继续使用
在单独的线程中阅读
,而(true)
,然后正常写作。@M.kazemAkhgary,谢谢你的建议,我正在考虑类似的事情。我还考虑到,这种方法可能需要自己的
超时
计数器,以防设备由于某种原因永久停止发送字节。另外,关于@Pierre的评论,我不确定我是否想让这个应用程序显式地多线程化。优点似乎很明显,但我担心潜在的缺点。谢谢你的周到回答和链接,非常感谢。我将研究它们。我还发现以下内容很有用:。你能详细说明一下信号量的用法吗?我的理解是信号量管理多线程访问,这在使用
wait
时不适用。它只适用于使用单独的
任务。Run()
,对吗?我同意我应该做更多的测试,这最终决定了必要的方法。这是一个很好的问题。我想到了以下场景:假设您向某个设备发送请求a,UI线程现在是自由的,设备忽略请求a,因为它的软件版本不支持它,异步请求a仍在处理中,您向设备发送请求B,设备响应请求B,因为它是一个有效的请求,现在UI线程处理对B的响应,就好像它是对a的响应一样。
Stream
有一些内部同步机制,但我不清楚确切的
SerialStream
行为。所以我会组织我这边的关键部分来确定。研究让我发现了一个关于同步和异步API的发现,请检查更新。