由于串口通信是异步的,我很早就想到了涉及与RS 232设备通信的项目,我必须让后台线程不断读取接收数据的端口。现在,我正在使用IronPython(.NET 4.0),因此我可以访问.NET内置的光滑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
足够简单。但正如我所提到的,我希望在新数据到达时不断检查此端口。理想情况下,只要数据等待,我就会让操作系统告诉我。 Whaddaya知道,我的祈祷已经回答,提供了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
。然后我的程序的其他部分查看linebuffer
以查看是否有任何完整的行等待使用。
现在,这显然是一个经典的生产者 - 消费者问题。制作人是BackgroundWorker
,将完整的行放入linebuffer
。消费者是一些代码,它们尽可能快地消耗linebuffer
中的那些行。
但是,消费者效率低下。现在他经常检查linebuffer
,每次都发现它是空的,感到失望;虽然每隔一段时间就会发现一条线在里面等待。优化这种方法的最佳方法是什么,以便消费者只有在有线路可用时醒来?这样,消费者不会不停地访问linebuffer
,这可能会引入一些并发问题。
另外,如果有一个更简单/更好的方式从串口不断阅读,我愿意接受建议!
答案 0 :(得分:3)
我不明白为什么你不能使用DataReceived
事件。正如文档所述
DataReceived事件不是 保证为每个字节提高 接收。使用BytesToRead属性 确定剩下多少数据 在缓冲区中读取。
所有这一切都表明,您无法保证为每个单独的数据字节获取单独的事件。您可能需要使用BytesToRead
属性检查并查看端口上是否有多个字节可用,并读取相应的字节数。
答案 1 :(得分:2)
没有必要使用旋转线程来轮询串行端口。
我建议使用SerialPort.BaseStream.BeginRead(...)方法。它比尝试在SerialPort类中使用事件要好得多。对BeginRead的调用立即返回并注册一个读取完成后调用的异步回调。在回调方法中,您调用EndRead并返回读入提供的缓冲区的字节数。 BaseStream(SerialStream)继承自Stream并遵循.Net Streams的一般模式,这非常有用。
但重要的是要记住,它是一个调用回调的.Net线程,因此您需要快速处理数据,或者将任何繁重的工作转移到您自己的线程之一。我强烈建议阅读以下链接,特别是备注部分。
http://msdn.microsoft.com/en-us/library/system.io.stream.beginread.aspx
答案 2 :(得分:1)
为什么在LineBuffer
另外,rom阅读文档,我相信它只是说读取100个字节!= 100个事件引发 - 它并不是说不能依赖这些事件。
答案 3 :(得分:1)
这是一些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的串行端口数据。