处理从串口读取的数据时串行端口线程锁定

时间:2014-01-07 00:51:20

标签: c# multithreading asynchronous serial-port locks

我有一个事件处理程序,只要在一个串口上收到数据就会被调用,一旦调用了这个事件,我处理一个单独的数据字节,直到处理好所有数据。

我的问题是,我如何确保在第一次调用字节时我不会异步调用此IBSerialPort_DataReceived事件处理程序,即我想保证ProcessByte方法只执行在一个线程上一次。

void IBSerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
  while (BytesToRead > 0)
  {
    ProcessByte((byte)ReadByte());
  }
}

2 个答案:

答案 0 :(得分:4)

这已经在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事件类型,这会使相当多的程序员绊倒。

你没有帮助。

答案 1 :(得分:2)

首先阅读您正在使用的库的文档 - 可以异步引发事件吗?如果没有,那么你就完成了。

更新:显然这个特殊事件不能同时提出,请参阅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的{​​{3}}事件中服务器缺乏WinRT文档。