1

我目前遇到一个问题,这似乎与关闭表单有关,而通过串行连接连接的秤不断发送数据(每 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,则不会对已发送的数据进行额外处理。

我现在的问题如下:

似乎 Eventhandling 是一个单独的线程,这会导致单击取消按钮时出现死锁,因为即使处理了事件,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 数据)会导致致命的崩溃。

所以,也许有人可以向我解释究竟是什么导致了这个问题,或者可以提供解决方案。(也许会再次触发 Cancel-Clicking 事件,但这很丑陋)

问候!

我全靠你了 :)

//编辑:这是当前的代码

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

1 回答 1

2
   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() 仅适用于随机丢失数据。

于 2012-06-03T15:47:19.893 回答