如果我没有点击它,Windows主表单会减慢处理

Chwaitang.

成员
加入
4月4日,2021年
消息
8
编程经验
5-10
大家好。

我遇到了一个奇怪的问题,如下所示:
我正在使用Visual Studio社区2019运行C#Win表单应用程序。

主要表单将不断接收通过USB-CAN适配器使用CAN制造商提供的DLL文件。
我点击了一个按钮以选择我想要处理和显示的特定Can-ID消息。然后将单独的子表单打开
并对CAN消息中的参数进行解码,然后在线图中的绘图绘图接收和解码的变量。

此图表绘制Sub表单速度向下。我发现当我不连续地在主表单窗口上单击并按住主表单窗口时会发生这种情况。
当我在主表单窗口上单击并按住时,我可以看到Can-ID消息数与其时间戳以及CAN数据包的原始十六进制数据
显示在主表单的显示日志上,并正在更新和运行连续。

当我放手且不点击主表单窗口时,主表单显示日志中的更新会减慢,仅在每20 - 30加秒时更新一次。
但我可以看到消息计数是正确的,因为在显示日志中显示的每个更新之间的CAN消息计数之间存在一些差异。这表示
仍在显示日志窗口离子主表单中显示的连续更新之间仍在接收到CAN消息。

但在上面的情况下,Sub表单中的图表绘图减慢,并且由于子形式未接收在更新之间没有接收那些数据点,因此未绘制许多丢失的数据点。
每个数据点的时间戳从主文件发送到子形式,在子表单上显示,它也会在更新之间冻结。但是当我点击主表单窗口时,都是
显示日志窗口中的主题和子表单中的收到数据间隔时间戳顺利和连续运行。

我已经尝试了我知道的意思,并且没有结果没有结果。
请指教。

此致
 

jmplhinney.

C#论坛主持人
工作人员
加入
2011年4月23日
消息
3,720
地点
悉尼,澳大利亚
编程经验
10+
它听起来像系统正在为UI线程分配更低的优先级。这使得一定程度的感觉是,如果您没有专注于特定形式,则在存在其他线程时将资源分配给它是逻辑的。虽然有关必须单击并保持表单的部分听起来很有趣。表单必须活跃以获得最优先级,这也是如此,它也听起来,即使你没有直接与之交互,它也会有较低的优先级。这可能是你不能做任何事情的东西 - 它可能只是一个Windows的东西 - 但很难轻易测试。

至于缺少的数据,几乎肯定会与您的代码有关,但我们无法在没有看到相关代码(并且只有相关代码)的情况下更多地说更多。当我说的情况下,您可以在辅助线程上完成ui线程上的数据,但是,正如我所说,在没有具体的细节,难以知道。不幸的是,你与特定硬件相互作用的事实使得很难提供我们可以为自己测试的东西。
 

Chwaitang.

成员
加入
4月4日,2021年
消息
8
编程经验
5-10
抱歉,加入,
当单击并保持上表单中没有任何表单时,会发生速度。单击并持有任何一种形式(主要用于或任何执行图表图的子表单)将导致事项恢复正常(在主表单中平滑连续更新消息计数,并在子表单上接收数据时间戳)那

请指教。

 

Chwaitang.

成员
加入
4月4日,2021年
消息
8
编程经验
5-10
你好,

在这里我在下面发布相关代码:
C#:
private void btn_HS1_Click(object sender, EventArgs e)    // button click to start monitor HS1 CAN-ID msg (on main form)
{
    if (!HS1_diagnose)
    {
        this.btn_HS1.Text = "Stop HS1 log";
        HS1_diagnose = true;

        hs1Data = new Form_HS1mcuData();                        // Form_HS1mcuData class is in Form_HS1mcuData.cs
        //  hs1Data.FormClosing += new FormClosingEventHandler(Form_HS1_Closing);
        hs1Data.FormClosed += new FormClosedEventHandler(Form_HS1_Close);
        hs1Data.Show();                                                         // Initialisation of hs1Data is done in main form
    }
以下是form_hs1mcudata的表单加载事件:
C#:
public void Form_HS1mcuData_Load(object sender, EventArgs e)    (on HS1 form)
{
    if (!OpenHS1form)
    {
        OpenHS1form = true;
        Close_HS1Form = false;      

        thread_HS1 = new Thread(new ThreadStart(hs1_Diagnostic));    // child thread for HS1 form
        thread_HS1.Name = "HS1 Diagnostic";
        thread_HS1.Priority = ThreadPriority.Highest;
        thread_HS1.Start();
    }
这是HS1_DIAGNOSTIC线程功能
C#:
public void hs1_Diagnostic()      // (on HS1 form)
{
    string filePath, resultString;
    string oldChar = "\\";
    string newChar = "\\\\";

    this.BeginInvoke((System.Windows.Forms.MethodInvoker)delegate () {      // Timer to call chart plotting function periodically
        ChartTimer = new System.Windows.Forms.Timer();
        ChartTimer.Tick += new System.EventHandler(ChartTimer_Tick);
        ChartTimer.Interval = 10;
        ChartTimer.Enabled = true;
    });

    while (!Close_HS1Form)    // (on HS1 form)
    {
        this.Invoke((Action)this.ProcessMessage);   // This is main function to decode and parse received CAN msg
    }

    if (Close_HS1Form)
    {
        //while (WIP1) { };
        this.Invoke(new MethodInvoker(() => this.Close()));
        //this.Close();
    }
这里是处理和解码
C#:
private void ProcessMessage()     // (on HS1 form)
{
    if (HS1_canRead)
    {
        tempByte = HS1_data[1];
        unsignedINT16 = HS1_data[0];
        unsignedINT16 |= (ushort)(tempByte << 8);
        tempDouble = Convert.ToDouble(unsignedINT16);
        Torque_Feedback = tempDouble * 0.0625;
        HS1_Torque.Enqueue(Torque_Feedback);

        tempByte = HS1_data[5];
        signedINT16 = HS1_data[4];
        signedINT16 |= (short)(tempByte << 8);
        dc_Current = signedINT16;
        HS1_CurrentDC.Enqueue(dc_Current);

        tempByte = HS1_data[3];
        signedINT16 = HS1_data[2];
        signedINT16 |= (short)(tempByte << 8);
        motor_Speed = signedINT16;
        HS1_RPM.Enqueue(motor_Speed);

        HS1_Time.Enqueue(HS1_TimeStamp);
        this.Invoke((Action)this.addRecordHS1);    // Here is to save data to file
        HS1_PlotOK = true;
        HS1_canRead = false;
    }
}
这是定时器勾选到绘图图//(在HS1表格上)
C#:
private void ChartTimer_Tick(object sender, System.EventArgs e)   // Timer method
{
    if (!Close_HS1Form)
    {
        if (HS1_PlotOK)
        {
            if(HS1_Time.Count != 0)
            {
                qTorque_Feedback = Convert.ToDouble(HS1_Torque.Dequeue());
                qmotor_Speed = Convert.ToInt16(HS1_RPM.Dequeue());
                qdc_Current = Convert.ToInt16(HS1_CurrentDC.Dequeue());
                qHS1_TimeStamp = Convert.ToString(HS1_Time.Dequeue());
                this.Invoke((Action)this.update_chart1);    // update of individual chart
                this.Invoke((Action)this.update_chart2);
                this.Invoke((Action)this.update_chart3);

                this.Invoke(new MethodInvoker(() => textBox1.Text = qHS1_TimeStamp));
            }       

            HS1_PlotOK = false;           
        }
    }
}
以下是按钮单击以关闭HS1表格//(在HS1表格上)
C#:
private void btn_CloseHS1_Click(object sender, EventArgs e)
{
    Close_HS1Form = true;
}
以下是用于访问HS1表单中的CAN类结构的方法( - 用于访问HS1形式和主形式的相同数据)
C#:
public class MCU_Message             // (on HS1 form)
{
    /// 11/29-bit message identifier
    public string ID { get; set; }
    /// Type of the message
    public Peak.Can.Basic.TPCANMessageType MSGTYPE { get; set; }
    public string MCU_TimeStamp;
    public bool msg_avail;
    public bool readyforPlot;
    public byte LEN { get; set; }
    public byte[] data { get; set; }

    public MCU_Message(string id, bool msg, bool ready, TPCANMessageType id_type, byte len, string Period)
    {
        ID = id;
        MSGTYPE = id_type;
        MCU_TimeStamp = Period;
        LEN = len;
        msg_avail = msg;
        readyforPlot = ready;
        data = new byte[len];
    }
}

public byte[] HS1_data      // (on HS1 form)
{
    get { return HS1_Message.data; }
    set { HS1_Message.data = value; }
}

public bool HS1_canRead
{
    get { return HS1_Message.msg_avail; }
    set { HS1_Message.msg_avail = value; }
}
以下是我知道传递给子表单的先前数据已被处理并读取新数据,然后传递给子表单
C#:
foreach (MessageStatus msg in m_LastMsgsList)    // this is in main form
{
    if ((msg.CANMsg.ID == theMsg.ID) && (msg.CANMsg.MSGTYPE == theMsg.MSGTYPE))
    {
        msg.Update(theMsg, itsTimeStamp);   // update display log in main form

        //if (HS1_diagnose && !hs1Data.HS1_canRead)   // if i use this if condition check, the program is not stable and sometimes give error of object instance not initialised
        if (HS1_diagnose && hs1Data != null)                 // which is why i switch to hs1Data != null
        {
            decodeMessageMCU(msg, itsTimeStamp, hs1Data.HS1_ID, TPCANMessageType.PCAN_MESSAGE_EXTENDED);
            hs1Data.HS1_canRead = true;                  
        }
        return;
    }
}
这是DecodeMessagemcu,也是主要形式
C#:
// We filter the received message looking for waht we want
if ((msg.CANMsg.ID == Convert.ToUInt32(id_searched, 16)) && (msg.CANMsg.MSGTYPE == id_type_searched) && (msg.MarkedAsUpdated))
{
    switch (msg.CANMsg.ID)
    {
        case 421068657:     // Torque, Speed
            hs1Data.HS1_TimeStamp = msg.TimeString;
            for (int i = 0; i < hs1Data.HS1_len; i++)
                hs1Data.HS1_data[I] = msg.CANMsg.DATA[I];
            msg.MarkedAsDecoded = true;
            break;
    }
}
以前,当我将所有数据放在主表单上时,我的方法是凌乱的,以主形式解码它们,然后将变量传递给子表单以绘制图表。然后我没有遇到这个问题。
现在,主要表单只是收到消息并将原始数据传递给Dea表单,该子表单进行解码和绘图。

对不起,如果我发布了这么多,但我真的不知道上述问题发生在我身上以及如何解决这个问题。

任何建议都受到赞赏。
谢谢
 
最后编辑了主持人:

jmplhinney.

C#论坛主持人
工作人员
加入
2011年4月23日
消息
3,720
地点
悉尼,澳大利亚
编程经验
10+
不幸的是那里的代码太多了。所有这一点实际上都不是相关的,它将需要我们,他们无法访问或经验特定的硬件,太多时间和精力来解决正在发生的事情。为自己和我们来说,您应该创建一个尽可能简单的新测试项目,但仍然演示了这个问题。这通常会为您提供对实际发生的事情,从而使您能够至少部分解决您自己的问题,但绝对意味着我们可以识别发生的事情以及更有效的问题。
 

Chwaitang.

成员
加入
4月4日,2021年
消息
8
编程经验
5-10
好的..是以下方法在子表单上运行的线程和main表单之间的线程之间交换数据吗?
C#:
public byte[] HS1_data      // (on HS1 form)
{
    get { return HS1_Message.data; }
    set { HS1_Message.data = value; }
}

public bool HS1_canRead
{
    get { return HS1_Message.msg_avail; }
    set { HS1_Message.msg_avail = value; }
}
数据驻留在子表单上。我超越了子形式和主要间歇之间的数据
 
最后编辑了主持人:

jmplhinney.

C#论坛主持人
工作人员
加入
2011年4月23日
消息
3,720
地点
悉尼,澳大利亚
编程经验
10+
请不要发布未格式化的代码片段,因为它们太难阅读,尤其是在移动设备上。我花了一些时间格式化#4,并通知你的变化,你在#6的帖子中做了相同。确保您只发布展示展示如可读性的问题和发布代码所需的最小代码就是尽可能多地完成您可以帮助您帮助您的部分和包裹。
 

jmplhinney.

C#论坛主持人
工作人员
加入
2011年4月23日
消息
3,720
地点
悉尼,澳大利亚
编程经验
10+
至于这个问题,您刚刚发布了几个属性的代码,因此没有发生数据交换。

In general, forms are just objects so passing data between forms or between forms and other objects is no different than passing any data between any two objects. The sticky part is that anything that relates directly to the UI, i.e. anything that makes use of the window handle of a form or control, must be done on the UI thread. Just passing data between forms doesn't involve the window handle so it doesn't matter what thread to do it on. Setting the Text of a TextBox, for instance, does involve the window handle because it involves modifying what you see in the UI, so that does need to be done on the UI thread.

如果您所做的一切都不需要很长时间,那么可以在UI线程上完成所有。只要您开始使用多线程来保持UI响应,您通常应该尽可能多地执行辅助线程,只有在实际需要更新UI时才向UI线程编组。如果您需要在UI上执行多个操作,则应汇编单个方法调用并一起执行所有这些操作。这是因为跨越螺纹边界来回往返是昂贵的。例如,这里:
C#:
this.Invoke((Action)this.update_chart1);    // update of individual chart
this.Invoke((Action)this.update_chart2);
this.Invoke((Action)this.update_chart3);

this.Invoke(new MethodInvoker(() => textBox1.Text = qHS1_TimeStamp));
当您只需制作一个然后在一次旅行时执行四个单独的ui线程即可单独的TRIP。
 

约翰

C#论坛主持人
工作人员
加入
2011年4月23日
消息
1,159
地点
挪威
编程经验
10+
您在UI线程上使用了很多调用,这表明您尚未考虑哪个线程。它基本上看起来你在UI线程上做所有事情,除了在HS1_diagnostic中循环时循环。

HS1_DIAGNOSTIC调用ProcessMessage到UI线程。

Forms Timer间隔10ms低于可能的精度: Timer类(System.Windows.Forms)
 

跳伞运动员

工作人员
加入
2019年4月6日
消息
2,892
地点
切萨皮克,va.
编程经验
10+
In hs1_Diagnotic() you are using a Windows Forms timer in a non-UI thread. The WinForms timer assumes that it is running on a UI thread, and that there is a message pump. You probably should be using one of the other timer variants for use on that non-UI thread. I suggest the System.Threading.Timer or the System.Timers.Timer.


除此之外,我怀疑发生了什么,即当您将鼠标持有下一个表单时,消息泵正在更快地驱动,因为它必须将所有这些鼠标递送到下窗消息。当您不按住鼠标时,并不像排队那么多消息,并且消息泵不必快速驱动。如果您只需将鼠标指向鼠标指针在两个表单中的一个中,您也怀疑您也会看到类似的速度,因为鼠标移动消息必须被解雇。
 

约翰

C#论坛主持人
工作人员
加入
2011年4月23日
消息
1,159
地点
挪威
编程经验
10+
In hs1_Diagnotic() you are using a Windows Forms timer in a non-UI thread
它仅通过Control.Invoke在那里配置,因此也是UI线程。
 

跳伞运动员

工作人员
加入
2019年4月6日
消息
2,892
地点
切萨皮克,va.
编程经验
10+
您实例化定时器问题的线程。是的,调用()调用最终驱动UI更新以发生在UI线程上,但控件具有线程关联。
 

跳伞运动员

工作人员
加入
2019年4月6日
消息
2,892
地点
切萨皮克,va.
编程经验
10+
哦!我明白你想要说的话。无论如何,BeginInvoke()最终会在UI线程中创建计时器。
 

jmplhinney.

C#论坛主持人
工作人员
加入
2011年4月23日
消息
3,720
地点
悉尼,澳大利亚
编程经验
10+
我建议,现在最好的选择是返回绘图板并重新开始。首先不是写任何代码。在您编写代码之前执行一些计划,以便您知道在写它之前要做什么。考虑所涉及的步骤以及所需的顺序。您可以将任务标记为UI绑定或不在该阶段。然后,您可以清楚地了解在哪个线程上执行的内容以及为什么。这样,您将不会最终执行愚蠢的事情,就像在主线程上显然被执行的代码调用调用。基本上,你让自己变成了一个纠结,再次从一开始就比未决的东西更容易,而不是解开你拥有的东西。你可以沿着方式提问,而不是要求我们在最后清理混乱。
 

Chwaitang.

成员
加入
4月4日,2021年
消息
8
编程经验
5-10
我尝试使用数据线程处理分开UI线程处理。显示日志在主窗口时更新,但与其他笔记本电脑上运行的相同程序相比仍然慢,在显示日志上显示快速和平滑的更新。它可能是特定的笔记本电脑有操作系统/程序优先级吗?与其他笔记本电脑上运行的相同程序始终显示与其他笔记本电脑(发送相同的周期性可以信息)的延迟(我使用Windows Form Timer) - 意思是如果我想发送CAN EVERERY 10MSEC,则需要20毫秒间隔相邻的传输可以消息,但是这是OK COS我在接收器侧接收了每20毫秒。但是当我使用其他笔记本电脑时,相邻传输的CAN消息之间的时间间隔平均接近10毫秒。现在,在其他笔记本电脑上运行时,一切似乎都可以,没有慢速显示日志更新,在其他笔记本电脑上运行时没有滞后。
 

跳伞运动员

工作人员
加入
2019年4月6日
消息
2,892
地点
切萨皮克,va.
编程经验
10+
笔记本电脑的任何机会都有CPU跑步一直都是吗?
 
最佳 底部