2012-01-17 50 views
3

我想在C#中編寫一個簡單的多線程程序。它有一個按鈕,它在窗體上創建一個新的標籤,然後一個for循環運行顯示標籤中的循環值。所以如果你按下按鈕3次,它會在循環的窗體上創建3個帶有3個標籤的線程。簡單的多線程程序在C#不工作

當我按下按鈕一次,它工作正常。但是,當我按下它不止一次創造更多的標籤多,它運行到以下問題:

  1. 由於作爲按鈕被按下不止一次很快,它停在前面的線程循環,並運行新的線程循環。如果它是多線程的,那麼它不應該停止第一個循環。

  2. 當第二個標籤的循環結束,它給了以下錯誤

  3. 不設置到對象

    這裏是我的完整代碼的實例

對象引用。引發錯誤的行在最後「mylabel [tcount] .Text = i.ToString();」。

截圖程序:http://i.imgur.com/IFMIs.png

的代碼截圖http://i.imgur.com/sIXtc.png

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

     private int tcount = 0; 
     private int y_point = 0; 

     Thread[] threads = new Thread[5]; 
     Label[] mylabel = new Label[5]; 

     private void button1_Click(object sender, EventArgs e){ 
      threads[tcount] = new Thread(new ThreadStart(work)); 
      threads[tcount].Start(); 
     } 

     private void work(){ 
      if (this.InvokeRequired){ 
       this.Invoke(new MethodInvoker(delegate{ 
        mylabel[tcount] = new Label(); 
        mylabel[tcount].Text = "label" + tcount; 
        mylabel[tcount].Location = new System.Drawing.Point(0, y_point + 15); 
        y_point += 25; 

        this.Controls.Add(mylabel[tcount]); 
        for (int i = 0; i < 10000; i++){ 
         mylabel[tcount].Text = i.ToString(); 
         Application.DoEvents(); 
        } 
       })); 
      } 
      tcount++; 
     } 
     } 
    } 
+1

您不能在主UI線程AFAIK以外的線程中更新'mylabel [tcount] .Text'。 – 2012-01-17 12:51:28

+1

我認爲你在這裏遇到了交叉線程問題,你不應該從一個單獨的線程更新任何UI元素。 [檢查出來](http://stackoverflow.com/questions/661561/how-to-update-gui-from-another-thread-in-c) – musefan 2012-01-17 12:52:12

+2

我建議你閱讀[喬阿爾巴哈里的電子書](http:///www.albahari.com/threading/) - 這是一個很好的介紹 – 2012-01-17 12:54:34

回答

3

問題是tcount的範圍,因爲所有線程都訪問它的同一個實例,所以一旦第二個線程啓動,第一個線程也會進入第二個標籤。

另外你調用你的整個輔助方法,這將讓它在UI線程再次運行 - >實際上不是多線程...

您的輔助方法應該是這個樣子:

private void work() 
{ 
    int tIndex = tCount; //store the index of this thread 
    tcount++; 
    mylabel[tIndex] = new Label(); 
    mylabel[tIndex].Text = "label" + tcount; 
    mylabel[tIndex].Location = new System.Drawing.Point(0, y_point + 15); 
    y_point += 25; 

    Invoke((MethodInvoker)delegate() { this.Controls.Add(mylabel[tIndex]); }); 

    for (int i = 0; i < 10000; i++) 
    { 
     //doWork 
     Invoke((MethodInvoker)delegate() { mylabel[tIndex].Text = i.ToString(); }); 
    } 
} 
+0

你的帖子教會了我一些關於不調用整個工人方法的新東西。我現在要記住。並使用你的代碼,現在我沒有得到任何錯誤。但它看起來並不像多線程,似乎我仍然需要對它進行一些調整。 – Ali 2012-01-18 04:28:36

9

如果是多線程的,那麼它不應該停止第一個循環。

但它不是多線程的。

this.Invoke(新MethodInvoker(代表{

這通過調用上下文切換回UI線程,所以當你打開了很多後臺線程,你基本上然後把所有該處理返回到一個主線程

此:

Application.DoEvents();

然後給其他排隊的工作一個機會。仍然只在UI線程上。

最後你永遠不會參數化線程,所以他們都工作在相同的變量。只有一個名爲tCount - bang的非線程保存(不鎖定,不可變)變量。

基本上你證明:

  • 你的問題是不可解的多線程 - 任何UI元素進行操縱UI線程上發生(這就是爲什麼你調用),並因爲這是你要做的,你基本上可以不是多線程。
  • 您對UI程序如何處理線程和消息泵缺乏基本的瞭解。
  • 您對線程之間的變量scoing和訪問模式缺乏基本的瞭解。

返回閱讀文檔我會說。

+0

最後,你永遠不會參數化線程,所以它們都工作在相同的變量上。只有一個非線程保存(無鎖,無變化)變量名爲tCount - bang。 但我需要一個像tcount這樣的變量作爲全局的,所以我可以保持線程的數量。我如何在沒有tcount的情況下實現它? (回覆時我怎麼給換行符?) – Ali 2012-01-18 04:20:33

+0

雖然tCount,但你需要LOCK訪問它或使用原子操作AND(!)變量必須是VOLATILE - 否則線程可能不會讀取它,假設他們知道該值並優化存儲器訪問。 – TomTom 2012-01-18 05:50:41

0

Jep,你需要將tcount複製到局部變量。只要你在線程還沒有結束的時候按下按鈕兩次,它就會操作第二個按鈕。