2011-02-09 100 views
4
using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 
using System.Threading; 


namespace testThreads 
{ 
    public partial class Form1 : Form 
    { 
     public Form1() 
     { 
      InitializeComponent(); 

     } 

     private void Form1_Load(object sender, EventArgs e) 
     { 

     } 

     public void countToLots() 
     { 
      for (int i = 0; i < 10000000; i++) 
      { 
       textBox1.Text = "Counting to 10000000, value is " + i + Environment.NewLine; 
      } 
     } 

     public void countToZero() 
     { 
      for (int i = 10000000; i > 0; i--) 
      { 
       textBox2.Text = "Counting to 0, value is " + i + Environment.NewLine; 
      } 
     } 

     private void button1_Click(object sender, EventArgs e) 
     { 
      Thread countUp = new Thread(new ThreadStart(countToLots)); 
      Thread countDown = new Thread(new ThreadStart(countToZero)); 
      countUp.Start(); 
      countDown.Start(); 
     } 

     private void button2_Click(object sender, EventArgs e) 
     { 
      textBox3.Text = "Bobby bob bob " + Environment.NewLine; 
     } 
    } 
} 

我真的需要嘗試並得到這個 - 我只是不明白爲什麼我得到一個錯誤消息的理論背後。有人可以幫我嗎?Threading基礎知識

跨線程操作無效: 控制「textBox1的」從比它的線程上創建 以外的 線程訪問。

回答

5

UI控件具有「線程關聯」;他們不要想要被除UI線程之外的任何東西所觸及;包括讀寫屬性。應該通過使用InvokeBackgroundWorker從UI線程完成對.Text的分配。

例如:

public void countToLots() 
{ 
    for (int i = 0; i < 10000000; i++) 
    { 
     // running on bg thread 
     textBox1.Invoke((MethodInvoker) delegate { 
      // running on UI thread 
      textBox1.Text = "Counting to 10000000, value is " 
         + i + Environment.NewLine; 
     }); 
     // running on bg thread again 
    } 
} 

,這種類型的線程切換有開銷。您不應該每次迭代都回調 - 例如,您應該(例如)每隔[n]次迭代更新一次UI - 以上爲例,每10000次更新一次。

+0

我仍然以相同的方式調用該方法嗎? – tom 2011-02-09 10:44:17

2

您不能使用與創建(稱爲新建)控件的線程不同的線程使用Form控件的方法或屬性。

爲了做到這一點只是做:

public void countToLots() 
    { 
     for (int i = 0; i < 10000000; i++) 
     { 
      SetText("Counting to 10000000, value is " + i + Environment.NewLine); 
     } 
    } 

    public void SetText(string text) 
    { 

     if (this.textBox1.InvokeRequired()) 
     { 
      Action<string> auxDelegate = SetText; 
      this.BeginInvoke(auxDelegate,text); 
     } 
     else 
     { 
      this.textBox1.Text = text; 
     } 
    } 

什麼方法與BeginInvoke的做只是再次調用從創建控制線程使用setText方法可以。

1

好吧,關於爲什麼控件具有UI線程親和力的理論。

如果您編程時間足夠長,您會記得表格和快速應用程序開發不是標準的日子。在那些日子裏,僅僅把控制權轉移到一種形式中是非常罕見的......一切都是由舊學校完成的。

現在,在windows中,「老派」做事的方式涉及到定義WindowProc

WindowProc是一個被調用來處理應用程序消息的函數(注意我說的是,不是)。該函數在主程序線程上運行,負責處理應用程序收到的每條消息,包括用戶界面的繪製和刷新。

現在所有這些大部分都是自動化的,所以當你創建一個表單時,負責完成所有工作的代碼是自動生成的,你不必擔心這個......但它仍然存在。當然,如果負責繪製用戶界面及其所有控件的線程是主線程,那麼您將看到如何從其他線程更改事物可能會因競爭條件等而擾亂應用程序本身。另外,由於UI處理是自動生成的,因此您不能只將兩個標準線程使用的同步機制放在一起,因爲您只能訪問一個線程上的代碼,而不能訪問主窗口回調。

從某種意義上說,BeginInvoke將爲您做的事情是將消息傳遞給主線程,告訴它在時間合適時親切地處理委託,從而將執行委託給主線程。