2010-10-20 25 views
4

我一直在盯着這個線程一段時間,我相信我的思想已經關閉了。認爲要做的最好的事情,爲了更新UI TextBox中的時間,將創建我認爲是一個簡單的線程來獲取時間並將其發佈回UI控件。在與之戰鬥了一段時間之後,我感到沮喪,並認爲我可能會以其他方式增加時間。在冒險家的勇敢精神中,我又給了它一個機會。「簡單」C#時間線程似乎凍結了

我有一個類似的線程在應用程序的其他地方運行,需要一個列表並在TabControl中填充DataGridView。我一直認爲這個過程大致相同,但我錯過了一個關鍵部分。線程的全部低於:

private void displayTime() 
    { 
     while (true) 
     { 
      String time; 
      String date; 

      time = DateTime.Now.TimeOfDay.ToString(); 
      int len = time.IndexOf('.'); 
      time = time.Substring(0, len); 
      date = DateTime.Now.Date.ToString(); 
      len = date.IndexOf(' '); 
      date = date.Substring(0, len); 

      updateClock(time, date); 
     } 
    } 
private void updateClock(String time, String date) 
    { 
     if (InvokeRequired) 
     { 
      BeginInvoke(new timeDel(updateClock), new object[] {time, date}); 
      return; 
     }   

     ctrlTimeTxt.Text = time + "\n" + date; 
    } 

以上線程在不同的地方(在試圖調試)已經啓動,但目前在窗體的Shown事件處理程序。表格開始出現,但一切似乎都掛起了。當我在線程中放置一個斷點時,我可以無限步,但UI似乎永遠不會得到控制權。我錯過了什麼?我很樂意擴展任何被忽視的細節。

編輯:一個澄清:此線程正在開始時處理所示事件的功能。 顯示的事件的描述爲:每次首次顯示窗體時發生。我認爲這可能會消除UI線程太快調用的理論。

回答

5

的問題是,在這裏你的while循環生成的事件隊列被水淹沒,許多事件:

while (true) 
{ 
    // ... 
    updateClock(time, date); 
} 

而是在一個線程做這個的,你可以在定期間隔使用Timer生成事件併爲每個事件做你的方法的一步。這也意味着您不必使用Invoke,因爲計時器會在主線程上生成事件。

也有在你的代碼中的其他兩個錯誤:

  • 偶爾TimeOfDay.ToString()可以輸出在第二變化正好擊中一時間,在這種情況下的ToString()的結果會不會是「12:23 :34.00000「,但只是」12:23:34「這將導致您的方法拋出異常。
  • 您正在撥打DateTime.Now兩次,但兩個呼叫之間的值可能已更改。通常情況下,這並不重要,但隨着午夜的到來,它可能會顯示'23:59:99.999',但這一天顯示爲第二天。在這個應用程序中這可能不是一個重大的錯誤,但是你應該習慣於只調用DateTime.Now,當你想要一致的日期和時間值時。

要修復這些錯誤,並簡化代碼:

  • 添加一個計時器到表單中。
  • 在設計器中將計時器的Enabled屬性設置爲true
  • 此事件處理程序添加到Tick事件:

    private void timer_Tick(object sender, EventArgs e) 
    { 
        DateTime now = DateTime.Now; 
        textBox1.Text = now.ToString("HH:mm:ss\nyyyy-MM-dd"); 
    } 
    

您不妨adjust the format string根據您的需要。

+0

這似乎是它會正常工作。我不知道是否你是最初發布的嘗試Invoke()而不是BeginInvoke()(我相信這是...),但最終解決了我的問題。有沒有原因不使用Invoke()並用一個計時器來代替? – 2010-10-20 19:10:43

+0

@Rich Hoffman:你在淹沒事件隊列。之所以使用Invoke而不是BeginInvoke工作原因是因爲它會導致線程在生成下一個事件之前等待更新表單,因此事件隊列不會失去控制。但是,如果您還嘗試在同一時間執行其他操作,那麼您仍然不必要地通過大量的事件來發送事件隊列,這可能會導致應用程序的響應問題。 – 2010-10-20 19:24:50

+0

另一方面,定時器允許你控制事件的速度,加上你根本不需要額外的線程。這是一個更簡單的解決方案。 – 2010-10-20 19:30:14

4

這並不令人驚訝,這不是很好 - 您可以儘可能快地在UI線程消息隊列(使用BeginInvoke())中填充調用。您可能希望在您的displayTime()線程中放置一箇中斷(即使用System.Threading.Thread.Sleep()),以便UI線程有時間做一些工作。

+0

是的。我現在意識到Invoke和BeginInvoke之間的巨大差異是異步性質和同步性質。這是一個很大的「哎呀」,也是一個教訓。謝謝。 – 2010-10-20 19:09:07

+0

你真的不想做這個快速的香草Invoke() - 問題主要是你要求UI線程做事情的速度,而不是你要求做的事情同步或異步。 – twon33 2010-10-20 19:13:00

2

你怎麼稱displayTime

如果你的Form.Show()方法是這樣的

Show() 
{ 
    displayTime(); 
} 

然後爲Mark answered你無限阻止您的主UI線程。

我將開始一個新的線程

Show() 
{ 
    Action displayTimeAction = displayTime; 

    displayTimeAction.BegingInvoke(null, null); 
}