从多个线程更新GUI

时间:2019-04-29 07:20:29

标签: c#

我已经创建了一个用户控件。在控件中,我有两种方法-发送和接收一些数据。这些方法还会更新位于用户控件上的数据网格。

public void RunTX()
{    
    tx_run = new Thread(new ThreadStart(SendCanFrames));
    if (!tx_run.IsAlive)
    {
        tx_run.IsBackground = true;
        tx_run.Start();
    }

}

public void RunRX()
{    
    rx_run = new Thread(new ThreadStart(ReadCanFrames));
    if (!rx_run.IsAlive)
    {
        rx_run.IsBackground = true;
        rx_run.Start();
    }
}

private void ReadCanFrames()
{
    ushort prev_time_stamp = 0;

    while (running)
    {
        if (CanDevice != null)
            CanDevice.Read(ref rx_can_msg, 1, ref read_cnt);

        if (read_cnt == 1)
        {
            read_cnt = 0;

            dataGridViewCanRx.Rows[0].Cells[0].Value = rx_can_msg[0].Id.ToString("X");
            dataGridViewCanRx.Rows[0].Cells[1].Value = rx_can_msg[0].Size.ToString();
            dataGridViewCanRx.Rows[0].Cells[2].Value = BytesToString(rx_can_msg[0].Data);
             dataGridViewCanRx.Rows[0].Cells[3].Value = (rx_can_msg[0].TimeStamp - prev_time_stamp).ToString();

            prev_time_stamp = rx_can_msg[0].TimeStamp;
        }

        prev_time_stamp = rx_can_msg[0].TimeStamp;
    }
}

private void SendCanFrames()
{
    if (tx_can_msg.Length == 0) return;

    VSCAN_MSG[] l_msgs = new VSCAN_MSG[2];

    while (running)
    {
        for (int i = 0; i < tx_can_msg.Length; i++)
        {
            if (can_messages[i].CountRun < can_messages[i].CountMax)
            {
                can_messages[i].TimeStamp1 = DateTime.Now;
                interval = can_messages[i].TimeStamp1 - can_messages[i].TimeStamp2;

                if (interval.TotalMilliseconds >= can_messages[i].Period)
                {
                    can_messages[i].TimeStamp2 = DateTime.Now;

                     l_msgs[0] = tx_can_msg[i];

                     //send CAN frame
                     CanDevice.Write(l_msgs, 1, ref written_cnt);
                     // send immediately 
                     CanDevice.Flush();

                     can_messages[i].CountRun++;

                    dataGridViewCanTx.Rows[i].Cells[4].Value = can_messages[i].CountRun.ToString();

                }
            }
        }
    }
}

我在主窗体上放置了四个用户控件实例,并启动了所有四个控件。

private void buttonStartAll_Click(object sender, EventArgs e)
{
    int can_channel;
    for (can_channel = 0; can_channel < 4; can_channel++)
    {
        if (started[can_channel] == false)
        {
            if (connected[can_channel] == true)
            {
                switch (can_channel)
                {
                    case 0:
                        mainform.userControlCan1.RunTX();
                        mainform.userControlCan1.RunRX();
                        started[can_channel] = true;
                        break;
                    case 1:
                        mainform.userControlCan2.RunTX();
                        mainform.userControlCan2.RunRX();
                        started[can_channel] = true;
                        break;
                    case 2:
                        mainform.userControlCan3.RunTX();
                        mainform.userControlCan3.RunRX();
                        started[can_channel] = true;
                        break;
                    case 3:
                        mainform.userControlCan4.RunTX();
                        mainform.userControlCan4.RunRX();
                        started[can_channel] = true;
                        break;
                }
            }
        }
    }
}

所有程序都在运行,但是GUI冻结和数据网格通过跳转进行更新。为什么?

1 个答案:

答案 0 :(得分:0)

我建议您着眼于两个关键点:调用和Application.DoEvents。

第一个将帮助您在主线程而不是子线程中更新GUI。 看看:https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodbase.invoke?view=netframework-4.8

Application.DoEvents()方法告诉系统执行其他等待的作业,而不会阻塞某些循环。否则,更新GUI作业将被推迟并且GUI被阻止。这是有关此方法的信息:https://docs.microsoft.com/tr-tr/dotnet/api/system.windows.forms.application.doevents?view=netframework-4.8

private delegate void dlgUpdateRows(object[] rx_can_msg, int tID);

// Write actual type of rx_can_msg instead of object[] in method signature , second parameter should be your thread id if needed
private void UpdateRows(object[] rx_can_msg, int tID =0)
    {
        try
        {
            if (this.InvokeRequired)
            {
                object[] obj = new object[2];
                obj[0] = rx_can_msg;
                obj[1] = Thread.CurrentThread.ManagedThreadId;
                this.Invoke(new dlgUpdateRows(UpdateRows), obj);
            }
            else
            {
                //Here update your datagrid using rx_can_msg

            }
            //This row is important to avoid blocking
            Application.DoEvents();
        }
        catch (Exception ex)
        {
            //Do error handling
        }


    }