Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ajax/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在windows窗体应用程序中加入工作线程_C#_Multithreading_Forms - Fatal编程技术网

C# 在windows窗体应用程序中加入工作线程

C# 在windows窗体应用程序中加入工作线程,c#,multithreading,forms,C#,Multithreading,Forms,我的程序是这样工作的: 我按下一个单选按钮打开端口。 接下来,我按下一个按钮“Read”,它启动一个线程,该线程使用Port.ReadLine()从串行端口连续读取数据,并将其打印到文本框中 我有另一台收音机,它应该先加入线程,然后关闭端口;问题是打印工作进展顺利,直到UI冻结时关闭端口 public Form1() { mythread = new Thread(ReadFct); myPort = new SerialPort(

我的程序是这样工作的: 我按下一个单选按钮打开端口。
接下来,我按下一个按钮“Read”,它启动一个线程,该线程使用
Port.ReadLine()
从串行端口连续读取数据,并将其打印到文本框中
我有另一台收音机,它应该先加入线程,然后关闭端口;问题是打印工作进展顺利,直到UI冻结时关闭端口

public Form1()
        {
            mythread = new Thread(ReadFct);
            myPort = new SerialPort("COM3", 9600);
            myPort.ReadTimeout = 3500;
            InitializeComponent();
            foreach (var t in Constants.ComboParameters)
                this.paramCombo.Items.Add(t);
            radioClose.CheckedChanged += new EventHandler(radioButtonCheckedChanged);
            radioOpen.CheckedChanged += new EventHandler(radioButtonCheckedChanged);


        }
下面是附加到线程的函数

void ReadFct()
        {
            string aux = "";
            while (readCondition)
            {
                if (myPort.IsOpen)

                    aux = myPort.ReadLine();


                this.SetText(aux);
            }

        }
下面是单选按钮事件处理程序

 public  void radioButtonCheckedChanged(object sender,EventArgs e)
            {
                if (radioOpen.Checked && !myPort.IsOpen)
                    try
                    {

                        myPort.Open();
                        mythread.Start();
                    }
                    catch (Exception)
                    {
                        MessageBox.Show("Nu s-a putut deschide port-ul");
                    }

                if (radioClose.Checked && myPort.IsOpen)
                {
                    readCondition = false;
                    mythread.Join();

                    myPort.Close();

                    //  myPort.DataReceived -= DataReceivedHandler;

                }


 }
读取按钮功能:

 private void readbtn_Click(object sender, EventArgs e)
        {

            if (!myPort.IsOpen)
                MessageBox.Show("PORT NOT OPENED!");
            else
            {
                // myPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
                readCondition = true;
                if (!mythread.IsAlive)
                {
                    mythread = new Thread(ReadFct);
                    mythread.Start();
                }

            }
在从另一个线程更改控件时,我使用了MSDN的建议:

private void SetText(string text)
        {
            if (this.textBox1.InvokeRequired)
            {
                StringTb del = new StringTb(SetText);
                this.Invoke(del, new object[] { text });
            }
            else
                SetData = text;

        }

很难确切地知道你需要什么,因为缺乏一个好的例子来说明这个问题。也就是说,这里的问题是
Thread.Join()
方法会导致该线程停止执行任何其他工作,而用来调用该方法的线程就是处理所有用户界面的线程。更糟糕的是,如果您的端口从未收到另一个换行符,那么您等待的线程将永远不会终止,因为您一直在等待
ReadLine()
方法。更糟糕的是,即使您确实获得了一个换行符,如果在等待
线程.Join()
时发生这种情况,对
Invoke()
的调用将死锁,因为它需要UI线程来完成其工作,
Thread.Join()
调用阻止它获得UI线程

换句话说,您的代码有多个问题,其中任何一个都可能导致问题,但所有这些加在一起意味着它不可能工作

有各种各样的策略来解决这个问题,但最好的方法是使用
wait
。执行此操作的第一步是更改I/O处理,使其异步完成,而不是为其指定线程:

// Ideally, you should rename this method to "ReadFctAsync". I am leaving
// all names intact for the same of the example though.

async Task ReadFct()
{
    string aux = "";
    using (StreamReader reader = new StreamReader(myPort.BaseStream))
    {
        while (true)
        {
            aux = await reader.ReadLineAsync();

            // This will automatically work, because the "await" will automatically
            // resume the method execution in the UI thread where you need it.
            this.SetText(aux);
        }
    }
}
然后,不显式创建线程,只需通过调用上面的

public Form1()
{
    // In this approach, you can get rid of the "mythread" field altogether
    myPort = new SerialPort("COM3", 9600);
    myPort.ReadTimeout = 3500;
    InitializeComponent();
    foreach (var t in Constants.ComboParameters)
        this.paramCombo.Items.Add(t);
    radioClose.CheckedChanged += new EventHandler(radioButtonCheckedChanged);
    radioOpen.CheckedChanged += new EventHandler(radioButtonCheckedChanged);
}

public async void radioButtonCheckedChanged(object sender,EventArgs e)
{
    if (radioOpen.Checked && !myPort.IsOpen)
    {
        try
        {
            myPort.Open();
            await ReadFct();
            // Execution of this method will resume after the ReadFct() task
            // has completed. Which it will do only on throwing an exception.
            // This code doesn't have any continuation after the "await", except
            // to handle that exception.
        }
        catch (Exception)
        {
            // This block will catch the exception thrown when the port is
            // closed. NOTE: you should not catch "Exception". Figure out what
            // *specific* exceptions you expect to happen and which you can
            // handle gracefully. Any other exception can mean big trouble,
            // and doing anything other than logging and terminating the process
            // can lead to data corruption or other undesirable behavior from
            // the program.
            MessageBox.Show("Nu s-a putut deschide port-ul");
        }

        // Return here. We don't want the rest of the code executing after the
        // continuation, because the radio button state might have changed
        // by then, and we really only want this call to do work for the button
        // that was selected when the method was first called. Note that it
        // is probably even better if you just break this into two different
        // event handlers, one for each button that might be checked.
        return;
    }

    if (radioClose.Checked && myPort.IsOpen)
    {
        // Closing the port should cause `ReadLineAsync()` to throw an
        // exception, which will terminate the read loop and the ReadFct()
        // task
        myPort.Close();
    }
}
在上面,我完全忽略了
readbtn\u Click()
方法。由于缺乏良好的MCVE,不清楚按钮在整个方案中扮演什么角色。您似乎有一个单选按钮组(由两个按钮组成),用于控制端口是打开还是关闭。现在还不清楚为什么会有一个额外的常规按钮,它似乎也能够打开端口并开始阅读,而不依赖于收音机组


如果你想要那个额外的按钮,在我看来,它应该做的就是通过选中“打开”单选按钮来改变无线组的状态。然后让单选组按钮处理端口状态和读数。如果您需要关于如何将上面的代码示例与整个UI完全集成的更具体的建议,您将需要提供更多的细节,最好是在一个新问题中。这个新问题必须包括一个好的MCVE。

很难确切知道你需要什么,因为缺少一个好的例子来说明这个问题。也就是说,这里的问题是
Thread.Join()
方法会导致该线程停止执行任何其他工作,而用来调用该方法的线程就是处理所有用户界面的线程。更糟糕的是,如果您的端口从未收到另一个换行符,那么您等待的线程将永远不会终止,因为您一直在等待
ReadLine()
方法。更糟糕的是,即使您确实获得了一个换行符,如果在等待
线程.Join()
时发生这种情况,对
Invoke()
的调用将死锁,因为它需要UI线程来完成其工作,
Thread.Join()
调用阻止它获得UI线程

换句话说,您的代码有多个问题,其中任何一个都可能导致问题,但所有这些加在一起意味着它不可能工作

有各种各样的策略来解决这个问题,但最好的方法是使用
wait
。执行此操作的第一步是更改I/O处理,使其异步完成,而不是为其指定线程:

// Ideally, you should rename this method to "ReadFctAsync". I am leaving
// all names intact for the same of the example though.

async Task ReadFct()
{
    string aux = "";
    using (StreamReader reader = new StreamReader(myPort.BaseStream))
    {
        while (true)
        {
            aux = await reader.ReadLineAsync();

            // This will automatically work, because the "await" will automatically
            // resume the method execution in the UI thread where you need it.
            this.SetText(aux);
        }
    }
}
然后,不显式创建线程,只需通过调用上面的

public Form1()
{
    // In this approach, you can get rid of the "mythread" field altogether
    myPort = new SerialPort("COM3", 9600);
    myPort.ReadTimeout = 3500;
    InitializeComponent();
    foreach (var t in Constants.ComboParameters)
        this.paramCombo.Items.Add(t);
    radioClose.CheckedChanged += new EventHandler(radioButtonCheckedChanged);
    radioOpen.CheckedChanged += new EventHandler(radioButtonCheckedChanged);
}

public async void radioButtonCheckedChanged(object sender,EventArgs e)
{
    if (radioOpen.Checked && !myPort.IsOpen)
    {
        try
        {
            myPort.Open();
            await ReadFct();
            // Execution of this method will resume after the ReadFct() task
            // has completed. Which it will do only on throwing an exception.
            // This code doesn't have any continuation after the "await", except
            // to handle that exception.
        }
        catch (Exception)
        {
            // This block will catch the exception thrown when the port is
            // closed. NOTE: you should not catch "Exception". Figure out what
            // *specific* exceptions you expect to happen and which you can
            // handle gracefully. Any other exception can mean big trouble,
            // and doing anything other than logging and terminating the process
            // can lead to data corruption or other undesirable behavior from
            // the program.
            MessageBox.Show("Nu s-a putut deschide port-ul");
        }

        // Return here. We don't want the rest of the code executing after the
        // continuation, because the radio button state might have changed
        // by then, and we really only want this call to do work for the button
        // that was selected when the method was first called. Note that it
        // is probably even better if you just break this into two different
        // event handlers, one for each button that might be checked.
        return;
    }

    if (radioClose.Checked && myPort.IsOpen)
    {
        // Closing the port should cause `ReadLineAsync()` to throw an
        // exception, which will terminate the read loop and the ReadFct()
        // task
        myPort.Close();
    }
}
在上面,我完全忽略了
readbtn\u Click()
方法。由于缺乏良好的MCVE,不清楚按钮在整个方案中扮演什么角色。您似乎有一个单选按钮组(由两个按钮组成),用于控制端口是打开还是关闭。现在还不清楚为什么会有一个额外的常规按钮,它似乎也能够打开端口并开始阅读,而不依赖于收音机组


如果你想要那个额外的按钮,在我看来,它应该做的就是通过选中“打开”单选按钮来改变无线组的状态。然后让单选组按钮处理端口状态和读数。如果您需要关于如何将上面的代码示例与整个UI完全集成的更具体的建议,您将需要提供更多的细节,最好是在一个新问题中。这个新问题必须包含一个好的MCVE。

SetText
方法是……每次您进行更改时,此代码似乎会生成2个radioButtonCheckedChanged事件。我认为你的代码可以处理这个,但是。。。每个单选按钮都会为true->false和false->true生成一个事件,因此单击一个按钮,两个按钮都会创建相同的事件。我会将radioButtonCheckedChanged函数分为两部分(因为它实际上是两个分开的函数),线程被困在ReadLine()调用中,因此它永远不会看到停止运行的请求。除非设备正在发送数据,否则这在某种程度上是不可预测的。关闭()串行端口,这将轰炸ReadLine()调用,捕获异常。或者选择DataReceived事件而不是使用线程。感谢您提醒我我忘记了。我请求使用
SetText
方法,因为我怀疑您在内部使用了
Invoke
。如果
加入
工作线程并在中调用