串行端口异步读取(非阻塞)+线程

时间:2012-04-12 05:25:44

标签: c# asynchronous serial-port nonblocking

好吧,在C#中使用这个SerialPort控件,过去4天我一直在苦苦挣扎,没有令人满意的结果。让我解释一下:

我有一个设备(Arduino UNO Board)与ac#prog通信,模拟一个比例(简单的请求/响应模式),设备发送一个由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()?

2 个答案:

答案 0 :(得分:3)

这适用于少量数据。但是如果数据更大,就像传递一些http信息一样,那么队列大小可能还不够。所以我认为你应该使用非阻塞类型的架构。

答案 1 :(得分:1)

请参阅this answer了解如何实施发送方。

对于读取端使用专用线程,在该线程中从端口读取消息,将其排队在合适的并发数据结构(例如ConcurrentQueue)中并立即循环返回以等待下一个输入来自串口。

在单独的线程上使用队列中的输入。

可能有更有效的方法,但这个方法易于实施和万无一失。