【问题标题】:serial port thread locking while processing data read from serial port处理从串口读取的数据时串口线程锁定
【发布时间】:2014-01-24 13:43:56
【问题描述】:
我有一个事件处理程序,只要在串行端口上接收到数据,就会调用它,一旦调用此事件,我就会处理一个单独的数据字节,直到处理完所有数据。
我的问题是,我如何确保这个 IBSerialPort_DataReceived 事件处理程序不会再次被异步调用,而我正在从第一次调用它开始处理字节,即我想保证 ProcessByte 方法只执行一次一个线程。
void IBSerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
while (BytesToRead > 0)
{
ProcessByte((byte)ReadByte());
}
}
【问题讨论】:
标签:
c#
multithreading
asynchronous
serial-port
locks
【解决方案1】:
首先阅读您正在使用的库的文档 - 可以异步引发事件吗?如果没有,那么你就完成了。
更新:显然这个特定事件不能同时引发,请参阅 Hans Passant 的回答。
如果可以,一种策略是使用lock:
object myLock = new object();
void IBSerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
lock(myLock)
{
while (BytesToRead > 0)
{
ProcessByte((byte)ReadByte());
}
}
}
现在请注意您正在使用锁,如果您在其他地方锁定同一个实例,您可能会遇到死锁,或者如果您持有相同的锁一段时间,您将暂停处理......
除此之外:通常,文档会告诉您该事件是否可以同时引发,因此您将不得不处理重新进入,例如System.Threading.Timer 的 Tick 事件,尽管并非总是如此,例如服务器在DatagramSocket 的MessageReceived 事件中缺少WinRT 文档。
【解决方案2】:
这已经在 SerialPort 类内部联锁。如果您的事件处理程序仍在运行,则有一个内部锁可确保 DataReceived 事件不会再次触发。
你可以在Reference Source看到相关代码,我这里复现一下:
private void CatchReceivedEvents(object src, SerialDataReceivedEventArgs e)
{
SerialDataReceivedEventHandler eventHandler = DataReceived;
SerialStream stream = internalSerialStream;
if ((eventHandler != null) && (stream != null)){
lock (stream) {
// SerialStream might be closed between the time the event runner
// pumped this event and the time the threadpool thread end up
// invoking this event handler. The above lock and IsOpen check
// ensures that we raise the event only when the port is open
bool raiseEvent = false;
try {
raiseEvent = stream.IsOpen && (SerialData.Eof == e.EventType || BytesToRead >= receivedBytesThreshold);
}
catch {
// Ignore and continue. SerialPort might have been closed already!
}
finally {
if (raiseEvent)
eventHandler(this, e); // here, do your reading, etc.
}
}
}
}
注意此代码中的lock (stream) 语句。您还可以看到除非收到某些内容,否则不会调用您的 DataReceived 事件处理程序。而且您必须注意 SerialData.Eof 事件类型,这让相当多的程序员感到不安。
你不必帮忙。