2017-04-25 70 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。

相關問題