问题描述
我在尝试停止 SerialPort 时遇到了一种奇怪的行为:在取消订阅和调用 close
之后,DataReceived 事件继续触发!(请参阅以下代码中的 StopStreaming
).结果,在我的事件处理程序代码中,我收到了 InvalidOperationException 消息端口已关闭".
I'm experiencing a weird behavior while trying to stop a SerialPort: the DataReceived event continues to fire after unsubscribing and after calling close
! (see StopStreaming
in the following code). As a result, in my event handler code I get an InvalidOperationException with the message that "The port is closed".
我错过了什么?关闭端口和停止事件的正确方法是什么?
What am I missing? What is the correct way to close the port and stop the events?
每次运行代码时都会出现此错误.因此,这不是随机发生的竞争条件,而是表明代码完全损坏的系统问题!但是,我看不到如何...
I get this error every time I run my code. So this is not a race condition that happens randomly but rather a systematic problem indicating a completely broken code! However, I fail to see how...
private SerialPort comPort = new SerialPort();
public override void StartStreaming()
{
comPort.Open();
comPort.DiscardInBuffer();
comPort.DataReceived += comPort_DataReceived;
}
public override void StopStreaming()
{
comPort.DataReceived -= comPort_DataReceived;
comPort.Close();
isStreaming = false;
}
private void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (e.EventType == SerialData.Chars)
{
SerialPort port = (SerialPort)sender;
int N = comPort.BytesToRead;
for (; N > 0; N--)
{
byte b = Convert.ToByte(comPort.ReadByte());
//... process b
}
}
}
根据建议,我将 StopStreaming
代码更改为如下内容:
following the suggestions, I changed StopStreaming
code to something like this:
public override void StopStreaming()
{
comPort.DataReceived -= comPort_DataReceived;
Thread.Sleep(1000);
comPort.DiscardInBuffer();
Thread.Sleep(1000);
comPort.Close();
isStreaming = false;
}
它现在似乎工作了,但我不是很高兴.我希望有一种更有效的方法来删除回调,而不是在程序中插入睡眠时间.
It seems to work now but I'm not really that happy. I wish there was a more effective way to remove the callback rather than inserting sleep periods in the program.
推荐答案
您的 DataReceived 事件处理程序在线程池线程上被调用.是的,他们有在不可预测的时间运行代码的尴尬习惯,这不是即时的.因此,如果设备正在主动发送数据,它可以与您的 Close() 调用竞争并在您关闭它之后运行,这是相当不可避免的.退订并不能解决问题,线程池线程已经得到了它的目标方法.
Your DataReceived event handler is called on a threadpool thread. And yes, they've got the awkward habit of running your code at an unpredictable time, it is not instant. So it is fairly inevitable that, if the device is actively sending data, that it can race with your Close() call and run after you closed it. Unsubscribing doesn't fix it, the threadpool thread already got its target method.
意识到你正在做什么来触发这个问题,你正在关闭端口设备正在发送数据.这不是很好,它肯定会导致数据丢失.但在调试代码时并非不可能发生,因为您实际上并不关心数据.
Do realize what you are doing to trigger this problem, you are closing the port while the device is sending data. That's not great, it is guaranteed to cause data loss. But not unlikely to happen when you are debugging your code since you don't actually care about the data.
一个对策是关闭握手,这样设备就不能再发送任何东西了.丢弃输入缓冲区.然后休眠一会儿,一两秒,以确保所有运行中的线程池线程都已完成运行.然后关闭端口.一个非常务实的做法是干脆不关闭端口,当您的进程终止时,Windows 会处理它.
A counter-measure is to turn off handshaking so the device cannot send anything anymore. Discard the input buffer. Then sleep for a while, a second or two, to ensure that any threadpool threads in-flight have completed running. Then close the port. A very pragmatic one is to simply not close the port, Windows will take care of it when your process terminates.
这篇关于SerialPort 在关闭后触发 DataReceived 事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!