2017-04-25 83 views
0

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

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的建议ST从另一个线程切换时控制:

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

     } 
+0

并且'SetText'方法是... –

+0

它看起来像这样的代码每次您进行更改时都会生成2个radioButtonCheckedChanged事件。我认为你的代码处理了这个问题,但是...每个单选按钮都会生成一个true-> false和false-> true的事件,所以你点击一个按钮并且两个按钮都会创建相同的事件。我会将radioButtonCheckedChanged函数分成两部分(因为无论如何它都是2个独立的函数)。 – David

+0

线程停留在ReadLine()调用中,因此永远不会看到请求停止运行。除非设备正在发送数据,否则有些可预测的情况不会发生。关闭()串口代替,这将炸毁ReadLine()调用,捕获异常。或者赞成DataReceived事件而不是使用线程。 –

回答

2

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

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

有很多策略来解决这个问题,但恕我直言,最好的是使用await。在这样做的第一步是改变你的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); 
     } 
    } 
} 

然后,而是明确创建线程,只是通过调用创建一个Task对象以上:

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_Click()方法。缺乏一个好的MCVE,目前还不清楚该按钮在整个方案中的作用。你似乎有一个单选按钮组(两个按钮),控制端口是打开还是关闭。目前还不清楚为什么您还有一个额外的常规按钮,它似乎也能够独立于收音机组打开端口并开始阅读。

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