代码之家  ›  专栏  ›  技术社区  ›  Aphex Bodhi Hu

不断地从带有后台线程的串行端口读取数据

  •  3
  • Aphex Bodhi Hu  · 技术社区  · 14 年前

    由于串行端口通信是异步的,我在我的项目的早期发现,涉及到与RS 232设备的通信,我将不得不有一个后台线程不断读取端口的数据接收。现在,我正在使用IronPython(.NET 4.0),这样我就可以访问.NET中内置的slick SerialPort类。我可以这样写代码:

    self.port = System.IO.Ports.SerialPort('COM1', 9600, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One)
    self.port.Open()
    reading = self.port.ReadExisting() #grabs all bytes currently sitting in the input buffer
    

    很简单。但正如我所提到的,我想在这个端口到达时不断地检查它是否有新的数据。理想情况下,我会有操作系统 告诉 只要有资料等着我。我的祈祷得到了回应 DataReceived 提供活动!

    self.port.DataReceived += self.OnDataReceived
    
    def OnDataReceived(self, sender, event):
        reading = self.port.ReadExisting()
        ...
    

    可惜这毫无价值, because this event isn't guaranteed to be raised !

    不能保证每个接收到的字节都引发DataReceived事件。

    那么,回到编写侦听器线程。我很快就完成了 BackgroundWorker 只是打电话 port.ReadExisting() 一次又一次。它在字节进入时读取字节,当它看到一行结束时( \r\n ),它将读取的内容放入 linebuffer . 然后我的程序的其他部分看看 行缓冲区 看看是否有完整的排队等候使用。

    现在,这是一个经典 生产者消费者 显然是个问题。制作人是 后台工作人员 ,将整行放入 行缓冲区 . 使用者是使用来自 行缓冲区 尽快。

    然而,消费者有点低效。现在他一直在检查 行缓冲区 ,每次都会失望地发现里面空无一人;尽管偶尔会发现里面有排队等候的人。什么是优化这一点的最佳方法,以便消费者只有在有线路可用时才醒来?这样的话,消费者就不会不停地访问 行缓冲区 ,这可能会引入一些并发问题。

    另外,如果有一种更简单/更好的方法从串行端口持续读取数据,我愿意接受建议!

    4 回复  |  直到 14 年前
        1
  •  3
  •   heavyd    14 年前

    我不明白你为什么不能用 DataReceived 事件。作为文档状态

    DataReceived事件不是 保证每个字节都会被提升 收到。使用BytesToRead属性 以确定要保留多少数据 在缓冲区中读取。

    所说的是,不能保证为每个单独的数据字节获得单独的事件。您可能需要使用 BytesToRead 属性并读取相应的字节数。

        2
  •  2
  •   coder    13 年前

    不需要有轮询串行端口的旋转线程。

    我建议使用SerialPort.BaseStream.BeginRead(…)方法。这比在SerialPort类中使用事件要好得多。对BeginRead的调用立即返回并注册一个异步回调,该回调在读取完成后调用。在回调方法中,调用EndRead并返回读取到所提供缓冲区的字节数。BaseStream(SerialStream)继承自Stream并遵循.Net Streams的通用模式,这非常有用。

    但必须记住,它是一个调用回调的.Net线程,因此您需要快速处理数据,或者将任何繁重的工作转移到自己的线程上。我强烈建议阅读以下链接,特别是评论部分。

    http://msdn.microsoft.com/en-us/library/system.io.stream.beginread.aspx

        3
  •  1
  •   Basic    14 年前

    为什么你的制作人不在 LineBuffer

    另外,rom读取文档,我相信它只是说读取100字节!=100个事件-并不是说这些事件是不可依赖的。

        4
  •  1
  •   dbasnett    14 年前

    下面是一些VB代码,它表达了我对应该如何实现这一点的看法:

    Dim rcvQ As New Queue(Of Byte()) 'a queue of buffers
    Dim rcvQLock As New Object
    
    Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, _
                                         ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _
                                     Handles SerialPort1.DataReceived
    
        'an approach
        Do While SerialPort1.IsOpen AndAlso SerialPort1.BytesToRead <> 0
            'what if the number of bytes available changes at ANY point?
            Dim bytsToRead As Integer = SerialPort1.BytesToRead
            Dim buf(bytsToRead - 1) As Byte
    
            bytsToRead = SerialPort1.Read(buf, 0, bytsToRead)
    
            'place the buffer in a queue that can be processed somewhere else
            Threading.Monitor.Enter(rcvQLock)
            rcvQ.Enqueue(buf)
            Threading.Monitor.Exit(rcvQLock)
    
        Loop
    
    End Sub
    

    值得一提的是,与此非常类似的代码已经以接近1Mbps的速度处理了串行端口数据。