在 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;
        }
    }

在 COM 数据接收事件不断触发时终止表单.C#.

   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() 只适合随机丢失数据。