C# 多线程 UI线程通信

一、概述

        在Winform界面程序中,系统创建了UI线程启动Form窗体,而我们创建其他的线程用于处理逻辑,当这些逻辑线程涉及到操作UI线程中的控件对象时则应考虑避免“线程安全”等问题。

        错误示例:

        private Thread LogicThread;                      // 逻辑线程

        private void Form1_Load(object sender, EventArgs e)
        {
            // 线程创建
            LogicThread = new Thread(new ThreadStart(LogicThreadMain));
            LogicThread.Start();
        }

        /// <summary>
        /// 线程方法
        /// </summary>
        private void LogicThreadMain()
        {
            // 错误示例
            // 有可能报出Exception
            textBox1.Text = "Error";
        }

        以下介绍两种解决方案:

  1. Invoke和BeginInvoke
  2. SynchronizationContext

二、Invoke和BeginInvoke

        .NET的控件(Control)提供了Invoke和BeginInvoke两种委托用于将控件对象的处理操作加入消息队列。

  • Invoke为同步委托,调用线程需要等待控件对象处理完后才可以继续执行。
  • BeginInvoke为异步委托,调用线程向控件对象发送了处理操作后即继续执行,无需等待。
        private Thread LogicThread;                      // 逻辑线程

        private void Form1_Load(object sender, EventArgs e)
        {
            // 线程创建
            LogicThread = new Thread(new ThreadStart(LogicThreadMain));
            LogicThread.Start();
        }

        /// <summary>
        /// 线程方法
        /// </summary>
        private void LogicThreadMain()
        {
            // 异步处理
            this.BeginInvoke((MethodInvoker)(() => { textBox1.Text = "BeginInvoke"; }));

            // 其他处理
            Thread.Sleep(1000);

            // 同步处理
            this.Invoke((MethodInvoker)(() => { textBox1.Text = "Invoke"; }));
        }

三、SynchronizationContext

        .NET的Threading中提供了SynchronizationContext类实现一个线程到另一个线程的同步上下文通信。

        SynchronizationContext提供了Send(SendOrPostCallback, object)和Post(SendOrPostCallback, object)两个通知方法将消息传播到目标的同步上下文。

  • Send(SendOrPostCallback, object)为同步方法,调用线程需等待该方法执行完毕才能继续执行。
  • Post(SendOrPostCallback, object)为异步方法,调用线程不需要等待执行完毕即继续执行。
        private SynchronizationContext context;          // 同步上下文
        private Thread LogicThread;                      // 逻辑线程

        private void Form1_Load(object sender, EventArgs e)
        {
            // 获取UI线程的同步上下文
            context = SynchronizationContext.Current;

            // 线程创建
            LogicThread = new Thread(new ThreadStart(LogicThreadMain));
            LogicThread.Start();
        }

        /// <summary>
        /// 线程方法
        /// </summary>
        private void LogicThreadMain()
        {
            // 异步处理
            context.Post(ShowText,"Post");

            // 其他处理
            Thread.Sleep(1000);

            // 同步处理
            context.Send(ShowText, "Send");
        }

        /// <summary>
        /// 在UI线程的同步上下文委托方法
        /// </summary>
        /// <param name="obj"></param>
        private void ShowText(object obj)
        {
            textBox1.Text = obj.ToString(); 
        }

四、异步委托

        基于上述两种方法,可以实现跨线程的UI控件操作与通信,实际应用中,异步实现UI更新的操作占多数,为此,为减少代码量,和大家分享一种我常用的基于异步委托BeginInvoke的解决方案。

        首先,创建一个静态类,写好常用控件的异步委托操作方法,如下:

    public static class BaseFunction
    {
        public static void ResetGridColor(this DataGridView grid)
        {
            foreach (DataGridViewColumn col in grid.Columns)
            {
                if (col.DefaultCellStyle.BackColor != Color.Empty)
                    col.DefaultCellStyle.BackColor = Color.Empty;
            }
        }

        public static void UpDownValue(this NumericUpDown ob, decimal Value)
        {

            if (ob.InvokeRequired)
            {
                ob.BeginInvoke((MethodInvoker)(() =>

                ob.UpDownValue(Value)
                ));
            }
            else
            {
                ob.Value = Value;
            }
        }


        public static void SetText(this Object ob, string sText)
        {
            Control cntl_OB = ((Control)ob);
            if (cntl_OB != null)
            {
                if (cntl_OB.InvokeRequired)
                {
                    cntl_OB.BeginInvoke((MethodInvoker)(() =>

                    ob.SetText(sText)
                    ));
                }
                else
                {
                    ((Control)ob).Text = sText;
                }
            }
        }

        public static void SetBackColor(this Object ob, Color cl)
        {
            Control cntl_OB = ((Control)ob);
            if (cntl_OB != null)
            {
                if (cntl_OB.InvokeRequired)
                {
                    cntl_OB.BeginInvoke((MethodInvoker)(() =>

                    ob.SetBackColor(cl)
                    ));
                }
                else
                {
                    ((Control)ob).BackColor = cl;
                }
            }
        }

        public static void SetForeColor(this Object ob, Color cl)
        {
            Control cntl_OB = ((Control)ob);
            if (cntl_OB != null)
            {
                if (cntl_OB.InvokeRequired)
                {
                    cntl_OB.BeginInvoke((MethodInvoker)(() =>
                    ob.SetForeColor(cl)
                    ));
                }
                else
                {
                    ((Control)ob).ForeColor = cl;
                }
            }
        }

        public static void SetFocus(this Object ob)
        {
            Control cntl_OB = ((Control)ob);
            if (cntl_OB != null)
            {
                if (cntl_OB.InvokeRequired)
                {
                    cntl_OB.BeginInvoke((MethodInvoker)(() =>
                    ob.SetFocus()
                    ));
                }
                else
                {
                    ((Control)ob).Focus();
                }
            }
        }

        public static void SetEnable(this Object ob, bool bEnable)
        {
            Control cntl_OB = ((Control)ob);
            if (cntl_OB != null)
            {
                if (cntl_OB.InvokeRequired)
                {
                    cntl_OB.BeginInvoke((MethodInvoker)(() =>
                    ob.SetEnable(bEnable)
                    ));
                }
                else
                {
                    ((Control)ob).Enabled = bEnable;
                }
            }
        }

        public static void SetVisible(this Object ob, bool bEnable)
        {
            Control cntl_OB = ((Control)ob);
            if (cntl_OB != null)
            {
                if (cntl_OB.InvokeRequired)
                {
                    cntl_OB.BeginInvoke((MethodInvoker)(() =>
                    ob.SetVisible(bEnable)
                    ));
                }
                else
                {
                    ((Control)ob).Visible = bEnable;
                }
            }
        }

        public static void SetBackGroundImage(this Object ob, Image img)
        {
            Control cntl_OB = ((Control)ob);
            if (cntl_OB != null)
            {
                if (cntl_OB.InvokeRequired)
                {
                    cntl_OB.BeginInvoke((MethodInvoker)(() =>
                    ob.SetBackGroundImage(img)
                    ));
                }
                else
                {
                    ((Control)ob).BackgroundImage = img;
                }
            }
        }

        public static void LayoutControlAdd(this TableLayoutPanel tb, Control Value, int column = 0, int row = 0)
        {
            if (tb.InvokeRequired)
            {
                tb.BeginInvoke((MethodInvoker)(() =>

                tb.LayoutControlAdd(Value, column, row)
                ));
            }
            else
            {
                tb.Controls.Add(Value, column, row);
            }
        }

        public static void ControlAdd(this TabControl tb, Control Value)
        {
            if (tb.InvokeRequired)
            {
                tb.BeginInvoke((MethodInvoker)(() =>
                ControlAdd(tb, Value)
                ));
            }
            else
            {
                tb.Controls.Add(Value);
            }
        }

        public static void ControlAdd(this Panel tb, Control Value)
        {
            if (tb.InvokeRequired)
            {
                tb.BeginInvoke((MethodInvoker)(() =>
                ControlAdd(tb, Value)
                ));
            }
            else
            {
                tb.Controls.Add(Value);
            }
        }

        public static void ControlAdd(this FlowLayoutPanel tb, Control Value)
        {
            if (tb.InvokeRequired)
            {
                tb.BeginInvoke((MethodInvoker)(() =>
                ControlAdd(tb, Value)
                ));
            }
            else
            {
                tb.Controls.Add(Value);
            }
        }

        public static void ControlAdd(this GroupBox tb, Control Value)
        {
            if (tb.InvokeRequired)
            {
                tb.BeginInvoke((MethodInvoker)(() =>
                ControlAdd(tb, Value)
                ));
            }
            else
            {
                tb.Controls.Add(Value);
            }
        }

        public static void ProgressBar_SetValue(this ProgressBar pg, int Value)
        {
            if (pg.InvokeRequired)
            {
                pg.BeginInvoke((MethodInvoker)(() =>

                pg.ProgressBar_SetValue(Value)
                ));
            }
            else
            {
                pg.Value = Value > 100 ? 100 : Value;
            }
        }

        public static void ComboBox_AddItem(this ComboBox cb, string sText)
        {
            if (sText != null)
                if (cb.InvokeRequired)
                {
                    cb.BeginInvoke((MethodInvoker)(() =>
                    cb.ComboBox_AddItem(sText)
                    ));
                }
                else
                {
                    cb.Items.Add(sText);
                }
        }

        public static void ComboBox_AddItems(this ComboBox cb, List<string> aText)
        {
            if (aText != null && aText.Count > 0)
                if (cb.InvokeRequired)
                {
                    cb.BeginInvoke((MethodInvoker)(() =>
                    cb.ComboBox_AddItems(aText)
                    ));
                }
                else
                {
                    cb.Items.AddRange(aText.ToArray());
                }
        }

        public static void ComboBox_Clear(this ComboBox cb)
        {
            if (cb.InvokeRequired)
            {
                cb.BeginInvoke((MethodInvoker)(() =>
                cb.ComboBox_Clear()
                ));
            }
            else
            {
                cb.Items.Clear();
            }
        }

        public static void ComboBox_SetSelectedIndex(this ComboBox cb, int index)
        {
            if (cb.InvokeRequired)
            {
                cb.BeginInvoke((MethodInvoker)(() =>
                cb.ComboBox_SetSelectedIndex(index)
                ));
            }
            else
            {
                cb.SelectedIndex = index;
            }
        }

        public static void ComboBox_SetSelectedItem(this ComboBox cb, Object ob)
        {
            if (cb.InvokeRequired)
            {
                cb.BeginInvoke((MethodInvoker)(() =>
                cb.ComboBox_SetSelectedItem(ob)
                ));
            }
            else
            {
                cb.SelectedItem = ob;
            }
        }

        public static void ClickVirtual(this Button bt)
        {
            if (bt.InvokeRequired)
            {
                bt.BeginInvoke((MethodInvoker)(() =>
                bt.ClickVirtual()
                ));
            }
            else
            {

                bt.PerformClick();
            }
        }

        public static void ClickVirtual(this RadioButton rd)
        {
            if (rd.InvokeRequired)
            {
                rd.BeginInvoke((MethodInvoker)(() =>
                rd.ClickVirtual()
                ));
            }
            else
            {
                rd.PerformClick();
            }
        }

        public static void NumericUpDown_SetValue(this NumericUpDown nud, decimal Value)
        {
            if (nud.InvokeRequired)
            {
                nud.BeginInvoke((MethodInvoker)(() =>
                nud.NumericUpDown_SetValue(Value)
                ));
            }
            else
            {
                nud.Value = Value;
            }
        }
    }

        之后,对应的控件就会多出一个上面自定义的静态方法,即可在任何线程上直接调用,实现异步操作,大大减少了代码量,提高代码易读性。

        private Thread LogicThread;                      // 逻辑线程

        private void Form1_Load(object sender, EventArgs e)
        {
            // 线程创建
            LogicThread = new Thread(new ThreadStart(LogicThreadMain));
            LogicThread.Start();
        }

        /// <summary>
        /// 线程方法
        /// </summary>
        private void LogicThreadMain()
        {
            // 异步处理
            textBox1.SetText("Test");
        }

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C#中,可以使用SerialPort类来进行串口通信。要实现多线程收发数据,可以使用以下步骤: 1. 创建一个SerialPort对象,设置好串口相关的属性(如波特率、数据位、停止位等)。 2. 在主线程中调用SerialPort对象的Open方法打开串口。 3. 创建一个新的线程,在该线程中循环读取串口数据,直到该线程被停止。 4. 在该线程中,可以使用SerialPort对象的Read方法读取串口数据,并通过回调方法将数据传递给主线程。 5. 在主线程中,可以使用委托来更新UI界面,显示串口收到的数据。 下面是一个示例代码: ```csharp using System; using System.IO.Ports; using System.Threading; public class SerialPortThread { private SerialPort serialPort; private Thread thread; private bool isRunning = false; public delegate void DataReceivedEventHandler(byte[] data); public event DataReceivedEventHandler DataReceived; public SerialPortThread(string portName, int baudRate) { serialPort = new SerialPort(portName, baudRate); serialPort.DataReceived += new SerialDataReceivedEventHandler(SerialPort_DataReceived); } public void Start() { if (!isRunning) { serialPort.Open(); isRunning = true; thread = new Thread(new ThreadStart(ReceiveThread)); thread.Start(); } } public void Stop() { if (isRunning) { isRunning = false; thread.Join(); serialPort.Close(); } } private void ReceiveThread() { while (isRunning) { if (serialPort.BytesToRead > 0) { byte[] buffer = new byte[serialPort.BytesToRead]; serialPort.Read(buffer, 0, buffer.Length); DataReceived?.Invoke(buffer); } Thread.Sleep(10); } } private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { // do nothing } } ``` 在主线程中,可以使用以下代码来创建并启动串口线程: ```csharp SerialPortThread serialPortThread = new SerialPortThread("COM1", 9600); serialPortThread.DataReceived += new SerialPortThread.DataReceivedEventHandler(OnDataReceived); serialPortThread.Start(); ``` 其中,OnDataReceived是回调方法,用于处理串口收到的数据。在该方法中,可以使用委托来更新UI界面,显示串口收到的数据。 ```csharp private void OnDataReceived(byte[] data) { this.Invoke(new Action(() => { textBox1.AppendText(Encoding.ASCII.GetString(data)); })); } ``` 在主线程中,可以使用以下代码来停止串口线程: ```csharp serialPortThread.Stop(); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值