简体   繁体   English

等待线程完成而不阻塞 UI 线程

[英]Wait for thread to finish without blocking UI thread

I am writing winForm app which have to listem SerialPort and print the data into multi line textbox in real time.我正在编写winForm应用程序,它必须列出SerialPort并将数据实时打印到多行textbox

Here is the code:这是代码:

using System;
using System.Drawing;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;
using System.ComponentModel;

namespace SCPListener
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
            GetAvailablePortNames();
            panel1.BackColor = ColorTranslator.FromHtml("#f44147");
            cbDefaultValue();
        }

        String[] PortNames;
        bool conn = true;
        String data;
        public string datetime;
        Form2 frm = new Form2();

        void GetAvailablePortNames()
        {
            PortNames = SerialPort.GetPortNames();
            comboBox1.Items.AddRange(PortNames);
        }


        void RefreshAvailablePortNames()
        {
            comboBox1.Items.Clear();
            PortNames = SerialPort.GetPortNames();
            comboBox1.Items.AddRange(PortNames);
        }
        public string getDataTime()
        {
            DateTime time = DateTime.Now;
            string date = time.ToString(@"hh\:mm\:ss");
            return date;
        }
        public void GetData()
        {
            string date = getDataTime();
            data = serialPort1.ReadLine();
            textBox1.AppendText("[" + date + "] " + "Received: " + data + "\r\n");
        }
        public void cbDefaultValue()
        {
            comboBox1.SelectedIndex = 0;
            comboBox5.SelectedIndex = 0;
            comboBox2.SelectedIndex = 0;
            comboBox3.SelectedIndex = 0;
            comboBox4.SelectedIndex = 0;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                if (comboBox1.Text == "" || comboBox2.Text == "" || comboBox3.Text == "" || comboBox4.Text == "" || comboBox5.Text == "")
                {
                    MessageBox.Show("Please port settings", "Incorrect port settings", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                else
                {
                    DateTime time = DateTime.Now;
                    string date = time.ToString(@"hh\:mm\:ss");
                    serialPort1.PortName = comboBox1.Text;
                    serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);
                    switch (comboBox3.Text)
                    {
                        case "5":
                            serialPort1.DataBits = 5;
                            break;
                        case "6":
                            serialPort1.DataBits = 6;
                            break;
                        case "7":
                            serialPort1.DataBits = 7;
                            break;
                        case "8":
                            serialPort1.DataBits = 8;
                            break;
                        default:
                            serialPort1.DataBits = 5;
                            break;
                    }
                    switch (comboBox4.Text)
                    {
                        case "None":
                            serialPort1.Parity = Parity.None;
                            break;
                        case "Odd":
                            serialPort1.Parity = Parity.Odd;
                            break;
                        case "Even":
                            serialPort1.Parity = Parity.Even;
                            break;
                        case "Mark":
                            serialPort1.Parity = Parity.Mark;
                            break;
                        case "Space":
                            serialPort1.Parity = Parity.Space;
                            break;
                    }
                    switch (comboBox5.Text)
                    {
                        case "One":
                            serialPort1.StopBits = StopBits.One;
                            break;
                        case "Two":
                            serialPort1.StopBits = StopBits.Two;
                            break;
                        case "OnePointFive":
                            serialPort1.StopBits = StopBits.OnePointFive;
                            break;
                    }
                    serialPort1.Open();
                    string CurrentPornName = comboBox1.Text;
                    label7.Text = "Opened " + CurrentPornName;
                    panel1.BackColor = ColorTranslator.FromHtml("#42f477");
                    comboBox1.Enabled = false;
                    comboBox2.Enabled = false;
                    comboBox3.Enabled = false;
                    comboBox4.Enabled = false;
                    comboBox5.Enabled = false;
                    //button5.Enabled = false;
                    //button6.Enabled = false;
                }
            }
            catch (Exception ex)
            {
                if (ex is UnauthorizedAccessException)
                {
                    MessageBox.Show("Unauthorized Acces","Access Error",MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                else if (ex is System.IO.IOException)
                {
                    MessageBox.Show("Please plug in your device", "IO Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            panel1.BackColor = ColorTranslator.FromHtml("#f44147");
            string CurrentPornName = comboBox1.Text;
            label7.Text = "Closed " + CurrentPornName; ;
            serialPort1.Close();
            comboBox1.Enabled = true;
            comboBox2.Enabled = true;
            comboBox3.Enabled = true;
            comboBox4.Enabled = true;
            comboBox5.Enabled = true;
            //button5.Enabled = true;
            //button6.Enabled = true;
        }

        private void button5_Click(object sender, EventArgs e)
        {
            RefreshAvailablePortNames();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            //backgroundWorker1.RunWorkerAsync();
            Thread thread = new Thread(start: ()=>
            {
                try
                {
                    datetime = getDataTime();
                    string date = getDataTime();
                    startListening:
                    if (serialPort1.BytesToRead > 0)
                    {
                        datetime = getDataTime();
                        data = serialPort1.ReadLine();
                        textBox2.AppendText("[" + datetime + "] " + "Received: " + data + "\r\n");
                    }
                    else
                        textBox2.AppendText("[" + datetime + "] " + "Error: There is no data to read" + "\r\n");
                    if (conn)
                        goto startListening;
                }

                catch (Exception ex)
                {

                    DateTime time = DateTime.Now;
                    string date = time.ToString(@"hh\:mm\:ss");
                    if (ex is TimeoutException)
                    {
                        textBox1.AppendText("[" + date + "] " + "Timuot Exception" + "\r\n");
                    }
                    else if (ex is InvalidOperationException)
                    {
                        MessageBox.Show("Plese check settings", "Error: Invalid Operation", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    else if (ex is System.IO.IOException)
                    {
                        MessageBox.Show("Port opened, but device unpluged..", "IO Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }

                }
            });
            thread.Start();
        }

        private void button4_Click(object sender, EventArgs e)
        {
            conn = false;
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
                try
                {
                    datetime = getDataTime();
                    string date = getDataTime();
                    startListening:
                    if (serialPort1.BytesToRead > 0)
                    {
                        datetime = getDataTime();
                        data = serialPort1.ReadLine();
                        textBox2.AppendText("[" + datetime + "] " + "Received: " + data + "\r\n");
                    }
                    else
                        textBox2.AppendText("[" + datetime + "] " + "Error: There is no data to read" + "\r\n");
                    if (conn)
                        goto startListening;
                }

                catch (Exception ex)
                {

                    DateTime time = DateTime.Now;
                    string date = time.ToString(@"hh\:mm\:ss");
                    if (ex is TimeoutException)
                    {
                        textBox1.AppendText("[" + date + "] " + "Timuot Exception" + "\r\n");
                    }
                    else if (ex is InvalidOperationException)
                    {
                        MessageBox.Show("Plese check settings", "Error: Invalid Operation", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    else if (ex is System.IO.IOException)
                    {
                        MessageBox.Show("Port opened, but device unpluged..", "IO Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }

                }

        }
    }
}

About Code:关于代码:

comboBox1 -> where the Ports are listed
comboBox2 -> where the Baud Rates options are listed
comboBox3 -> where the Stop Bits options are listed
comboBox4 -> where the Data Bits options are listed
comboBox5 -> where the Parity options are listed
button1 -> to connect chosen port
button2 -> to disconnect port
button3 -> to start listening and print data into textBox in real time
button4 -> to stop listening
button5 -> refresh available ports
panel1-> to identify port is connected or not (if connected it is green if it is not connected it is red)

So, what i want to do is that when I click the stop button ( button4 ), program have to stop listening.所以,我想要做的是当我点击停止按钮( button4 )时,程序必须停止收听。 I am trying to achieve this result using backgroundWorker .我正在尝试使用backgroundWorker来实现这个结果。 Using this code when i try to listen port (clicking start button button3 there is opening the small error window and tell please check settings (so it catching InvalidOperationException according to my code); If I wont use backgoundWorker and also remove try{...} cath{...} program start do display the received data from port but blocking the UI. Any ideas will be very helpful.当我尝试侦听端口时使用此代码(单击开始按钮button3会打开小错误窗口并告诉请检查设置(因此它根据我的代码捕获InvalidOperationException );如果我不使用backgoundWorker并删除try{...} cath{...} program start 确实显示从端口接收到的数据但阻塞了用户界面。任何想法都会非常有帮助。

button4_click needs to call backgroundWorker1.CancelAsync() instead of setting a bool. button4_click 需要调用 backgroundWorker1.CancelAsync() 而不是设置 bool。 With BackgroundWorkers, you need to specify that you want them to end and then check for it, setting and checking an outside bool will not work.使用 BackgroundWorkers,您需要指定您希望它们结束然后检查它,设置和检查外部 bool 将不起作用。 The try block in your backgroundWorker1_DoWork() method should look like the following. backgroundWorker1_DoWork() 方法中的 try 块应如下所示。 Keep in mind, it is easier for you and others to help you if you rename your variables (either done in the UI or by right clicking the variable and selecting 'Rename').请记住,如果您重命名变量(在 UI 中完成或通过右键单击变量并选择“重命名”),您和其他人可以更轻松地为您提供帮助。 Another note before the code, it is almost never ok to use 'goto', there is almost always a better option (a while loop in this case).代码前的另一个注意事项,使用 'goto' 几乎是不可能的,几乎总是有更好的选择(在这种情况下是 while 循环)。

datetime = getDataTime();
string date = getDataTime();
while (!backgroundWorker1.CancellationPending)
{
    if (serialPort1.BytesToRead > 0)
    {
        datetime = getDataTime();
        data = serialPort1.ReadLine();
        textBox2.AppendText($"[{datetime}] Received: {data}\r\n");
    }
    else
    {
        textBox2.AppendText($"[{datetime}] Error: There is no data to read\r\n");
    }
}

Edit to say: This should get you started, you will also want to check out this link to access your UI from backgroundWorker1, the problem is your UI is on one thread and your BackgroundWorker on another, threads do not work well together without some complimenting code编辑说:这应该让你开始,你还想查看这个链接从 backgroundWorker1 访问你的 UI,问题是你的 UI 在一个线程上,而你的 BackgroundWorker 在另一个线程上,如果没有一些赞美,线程不能很好地协同工作代码

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM