串行端口异步读取(非阻塞)+线程
本文关键字:线程 异步 读取 串行端口 | 更新日期: 2023-09-27 18:30:54
好吧,在过去的 4 天里,我一直在努力使用 C# 中的这个 SerialPort 控件,但没有令人满意的结果。让我解释一下:
我有一个设备(Arduino UNO Board)与模拟秤(简单请求/响应模式)的 c# 程序相通信,该设备发送由 3 个字节组成的命令序列(要求称重):CHR(27)+P+CHR(13),因此模拟器以模拟权重响应(我已经整理了设备如何捕获和解析此权重,因此问题不再存在)。使用 DataReceive 事件似乎我正在使用 Serialport.Read() 丢失数据,所以到目前为止我浪费了这种方法。模拟器必须始终侦听上述字节序列,并且必须具有GUI。我知道为此我必须使用线程来防止 GUI 被锁定(也许是后台工作者?)和一种在(现在)这 2 个线程之间共享的缓冲区,并防止线程同时读取/写入缓冲区(我需要状态机吗?(我寻求帮助,因为我不知道这是否是一个好方法,或者我的假设是错误的,或者是否有更简单的方法来解决这个问题)所以我正在寻求建议和(运气好)代码片段,或者如果你面临开发类似的应用程序你是如何解决它的。
如有必要,我可以提供到目前为止所做的代码以进一步澄清。希望你能对此有所了解。
提前谢谢。
更新 1
这是我到目前为止的代码:
ConcurrentQueue<byte> queue = new ConcurrentQueue<byte>();
....
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
bool listening = true;
while(listening)
{
if(serialPort.BytesToRead > 0)
{
byte b = (byte)serialPort.ReadByte();
queue.Enqueue(b);
}
}
}
因此,由于命令必须以字符 13(ASCII 中的 CR)结尾:
public string GetCommand()
{
string ret = "";
byte[] ba = new byte[1];
byte b = (byte)' ';
while(b!=13)
{
if(queue.TryDequeue(out b))
{
ba[0] = b;
ret += ASCIIEncoding.ASCII.GetString([ba]);
}
}
return ret;
}
为了测试这个 GetCommand() 方法,我从 buton_click 事件中的主 ui 线程调用它,但它挂起了应用程序,我是否需要创建另一个线程来调用 GetCommand() ?
这对于少量数据是可以的。但是,如果数据更大,例如传递一些 http 信息,则队列大小可能不够。所以我认为你应该使用非阻塞类型的架构。
有关如何实现发送端,请参阅此答案。
对于读取端,使用专用线程,在该线程中从端口读取消息,将其排队到合适的并发数据结构(例如ConcurrentQueue)中,并立即环回以等待来自串行端口的下一个输入。
在单独的线程上使用队列中的输入。
可能有更有效的方法,但这种方法易于实施且万无一失。