在 COM 数据接收事件不断触发时终止表单.C#.
本文关键字:终止 表单 数据 COM 事件 | 更新日期: 2023-09-27 18:36:05
我目前遇到了一个问题,这似乎与关闭表单有关,而通过串行连接连接的秤不断发送数据(每个 sek 大约 3 个包)。
我通过 DataReceived-Event 处理新数据(处理本身对于这个问题可能不感兴趣,因为我只是匹配数据)密切关注COM_InUse变量和 allowFireDataReceived 检查。
private void COMScale_DataReceived(object sender, EventArgs e)
{
if (allowFireDataReceived)
{
//set atomar state
COM_InUse = true;
//new scale:
if (Properties.Settings.Default.ScaleId == 1)
{
strLine = COMScale.ReadTo(((char)0x2).ToString());
//new scale:
Regex reg = new Regex(Constants.regexScale2);
Match m = reg.Match(strLine);
if (m.Success)
{
strGewicht = m.Groups[1].Value + m.Groups[2];
double dblComWeight;
double.TryParse(strGewicht, out dblComWeight);
dblScaleActiveWeight = dblComWeight / 10000;
//add comma separator and remove zeros
strGewicht = strGewicht.Substring(0, 1) + strGewicht.Substring(1, 2).TrimStart('0') + strGewicht.Substring(3);
strGewicht = strGewicht.Insert(strGewicht.Length - 4, ",");
//write to textbox
ThreadSafeSetActiveScaleText(strGewicht);
COMScale.DiscardInBuffer();
//MessageBox.Show(dblScaleActiveWeight.ToString(), "dblScaleActiveWeight");
}
}
//free atomar state
COM_InUse = false;
}
}
COM_InUse变量是一个全局布尔值,并"告诉"当前是否存在处理数据的过程。allowFireDataReceived 也是一个全局布尔值,如果设置为 false,则不会导致对已发送的数据进行额外处理。
我现在的问题如下:
事件处理似乎是一个单独的线程,这会导致在轻舐取消按钮时出现死锁,因为即使处理了事件,COM_InUse也永远不会变为 false(请参阅COMScale_DataReceived末尾,其中COM_InUse设置为 false)。虽然设置 allowFireDataReceived = false 工作得很好(不再处理),但正如我所说:while 循环不会终止。
private void bScaleCancel_Click(object sender, EventArgs e)
{
allowFireDataReceived = false;
while (COM_InUse)
{
;
}
if (!COM_InUse)
{
ret = 1;
SaveClose();
}
}
当我注释掉 while 块时,我必须在按钮上单击两次,但它可以正常工作而不会崩溃。由于这个用户非常不友好,我正在寻找一种安全关闭窗口的替代方法。
信息:简单地关闭(不检查 COM 数据是否已处理)会导致致命的崩溃。
所以,也许有人可以向我解释究竟是什么导致了这个问题,或者可以提供解决方案。(也许会再次触发取消点击事件,但这非常丑陋)
问候!
我指望你:)
//编辑:这是当前的代码
private void ThreadSafeSetActiveScaleText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.lScaleActive.InvokeRequired)
{
SafeActiveScaleTextCallback d = new SafeActiveScaleTextCallback(ThreadSafeSetActiveScaleText);
this.Invoke(d, new object[] { text });
}
else
{
this.lScaleActive.Text = text;
}
}
ThreadSafeSetActiveScaleText(strGewicht);
是的,DataReceived 事件在线程池线程上运行。 您已经知道这一点,否则您就不会称其为"ThreadSafe"。 我们看不到的是这种方法里面有什么。 但考虑到结果,您很可能正在使用 Control.Invoke()。
当您在 UI 线程上运行的代码中循环COM_InUse时,这将导致死锁。 Control.Invoke() 方法只有在 UI 线程执行了委托目标方法时才能完成。 但是 UI 线程只能在空闲时执行此操作,抽取消息循环并等待 Windows 消息。 并调用请求。 它在 Click 事件处理程序中循环时无法执行此操作。 所以 Invoke() 无法完成。 这使COM_InUse变量永远设置为 true。 这使得 Click 事件处理程序永远循环。 死锁城市。
调用 SerialPort.Close() 方法时会出现完全相同的问题,只有在处理完所有事件后才能关闭端口。
您需要改用 Control.BeginInvoke() 来解决此问题。 确保在委托目标开始执行时数据仍然有效。 例如,将其作为参数传递,必要时复制。
在秤无情地发送数据时关闭表单通常是一个问题。 在已释放的窗体上调用时,将出现异常。 若要解决此问题,您需要实现 FormClosing 事件处理程序并将 e.Cancel 设置为 true。 并取消订阅 DataReceived 事件并启动计时器。 将间隔设置为几秒钟。 当计时器滴答作响时,您可以再次关闭窗体,现在确保所有数据都已耗尽,并且不会再发生调用。
另请注意,调用 DiscardInBuffer() 只适合随机丢失数据。