2011-01-14 74 views
0

我正在編寫一個程序,它從串口接收數據並更新UI元素(如文本框,標籤等)。定時器用於頻繁向設備發送命令,作爲響應設備返回一些顯示的數據。一切安好。現在,如果我移動主表單或將其最小化,那麼接收代碼正在運行的線程退出(如我所想)。移動主窗體是否有可能導致線程退出?我的程序中可能存在什麼問題?爲什麼數據接收停止?可行的解決方案?線程退出問題

+2

您沒有發佈您的代碼。這是我無法幫助你的相同原因[上一次你問這個問題](http://stackoverflow.com/questions/4667272/receiving-data-from-serialport-stops-when-main-form-is-最小化或 - 移動)。 – 2011-01-14 06:49:36

回答

0

您的行爲(移動/最小化的形式)將不會影響在代碼中創建線程落後於大多數情況下。數據線更新UI(也許使用像myControl.Invoke)將正常工作,甚至要移動的形式或最小化形式(但如果你關閉窗體不中止更新線程,你會得到例外)。所以你不應該擔心這個。相反,我認爲您需要檢查移動事件背後的代碼或最小化表單事件。提供一些代碼有助於找出原因。

0

有一個稱爲線程調試窗口。打開斷點時打開程序並打開線程窗口。當您的線程正在接收數據時,應至少列出兩個線程ID,UI線程和線程獲取和接收串行數據。您可能想要命名您的線程,以便在調試器中更容易看到,否則只會有線程ID。從那裏應該給出一些線索,說明爲什麼或至少在退出時。

在你的線程代碼,以及你的UI代碼認沽斷點進行監控。這個問題相當模糊,但是調試和觀察線程應該有幫助。

1

您在resieving線程更新UI元素。

這意味着,你必須調用控件上UI更新調用,因爲它們不是在另一個線程創建的。

所以,我猜reciever的工作原理如下:

while(!Application.IsApplicationExiting) 
{ 
    if (!serial.IsReady) break; 
    ... 
    Data data = serial.GetData(); 
    string text = GetBaudRate(data); 
    ui.Invoke((Label label) => label.Text = value, txtBauldRateLabel); 
    ... 
} 

此時,你應該知道,在這種情況下,這個線程被阻塞,直到調用UI線程結束,但interesing部分當您嘗試調整大小或定位應用程序窗口時,對UI線程的更新也會阻止。

當您嘗試單擊並按住應用程序的某個欄上的鼠標右鍵時,您的UI線程塊會被阻止。

因此,您鎖定了接收器的主循環,在設備發生超時的情況下實際上不會被阻塞,這會引起接收器關閉(將串行狀態更改爲未就緒狀態)。

我建議你使用解鎖調用的BeginInvoke()重寫你的應用程序:

while(!Application.IsApplicationExiting) 
{ 
    ... 
    ui.BeginInvoke((Label label) => label.Text = value, txtBauldRateLabel); 
    ... 
} 
0

你崩潰的計時器線程,因爲你從中直接更新表單控件...你可以」不要那樣做!您必須調用Invoke(),以便您的代碼從主UI線程執行。

我在您的其他問題發佈了一個答案......這裏要再次重申:


我的猜測是,你崩潰的計時器線程,因爲你無法更新從另一個表單控件線程沒有問題...你必須創建一個在主UI線程下運行的委託。你通過測試Form.InvokeRequired來做到這一點,如果它是真的,則調用Form.Invoke

發生了什麼:您的計時器線程正在更新窗體上的文本框或其他控件。您調整大小或最小化,並且這些表單控件的句柄無效...您不能再使用它們了。除了你的計時器線程仍在運行並試圖使用它們。崩潰!

多線程窗體上的一個很好的例子是here。重要的部分是:

delegate void SetBoolDelegate(bool parameter); 

// This would be your timer tick event handler... 
void SetInputEnabled(bool enabled) { 

    if(!InvokeRequired) { 
     button1.Enabled=enabled; 
     comboBoxDigits.Enabled=enabled; 
     numericUpDownDigits.Enabled=enabled; 
    } 
    else { 
     Invoke(new SetBoolDelegate(SetInputEnabled),new object[] {enabled}); 
    } 

} 

在這個例子中,你測試了InvokeRequired。如果它是假的,那麼你在主UI線程上運行,並可以直接設置控件屬性。如果它是真的,你調用Invoke(),傳遞一個將從主UI線程調用的函數。

在此示例中,您調用的函數/委託與您所在的函數是相同的,但它不一定是。但是您可以將它傳遞給計時器滴答事件處理程序,讓它在主線程上執行。