2011-07-09 68 views
1
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 try1 
{ 
    public partial class Form1 : Form 
    { 
     volatile bool start_a = false; 
     volatile bool start_b = false; 
     public Form1() 
     { 
      InitializeComponent(); 
     } 

     private void button1_Click(object sender, EventArgs e) 
     { 
      if (start_a == false) 
      { 
       button1.Text = "Running"; 
       start_a = true; 
       Thread thread2 = new Thread(new ThreadStart(th1)); 

       thread2.Start(); 
      } 
      else 
      { 
       button1.Text = "Click to start"; 
       start_a = false; 
      } 

     } 

     void th1() 
     { 
     int a=0; 
     while (start_a==true) 
     { 

      label1.Invoke((MethodInvoker)(() => label1.Text = Convert.ToString(a))); 
      Thread.Sleep(50); 
      a++; 
     } 



     } 

     void th2() 
     { 
      int b = 0; 
      while (start_b == true) 
      { 
       label2.Invoke((MethodInvoker)(() => label2.Text = Convert.ToString(b))); 
       Thread.Sleep(5000); 
       b=b+5; 
      } 



     } 

     private void button2_Click(object sender, EventArgs e) 
     { 
      if (start_b == false) 
      { 
       button2.Text = "Running"; 
       start_b = true; 
       Thread thread2 = new Thread(new ThreadStart(th2)); 

       thread2.Start(); 
      } 
      else 
      { 
       button2.Text = "Click to start"; 
       start_b = false; 
      } 
     } 

     private void quitting(object sender, FormClosingEventArgs e) 
     { 
      start_a = false; 
      start_b = false; 
     } 


    } 
} 
+2

你的問題是什麼? – bdonlan

+0

您必須在關閉表單之前停止這些線程。 –

+0

+1因爲問題是現實世界。但下一次明確指出你的問題。我必須編譯和測試自己才能找到它。 – ceztko

回答

-1

我認爲問題是,你正在更新上比其所創建的線程的線程上UI控件不能在一個控件調用;我想你應該看看這個:How to update the GUI from another thread in C#?或這裏:http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired.aspx一些例子比需要更復雜,但其要點是你必須從它創建的同一個線程更新控制; Control.InvokeRequired是你想要關注的。

+0

他已經在使用Invoke作爲該問題的答案 – bdonlan

+0

@bdonian我的不好 – gangelo

1

我們需要更多關於發生錯誤的位置和時間的詳細信息。我通過查看代碼的第一個猜測是,當你試圖關閉表單時你會得到異常。退出事件處理程序將start_astart_b設置爲false,但不會等到後臺線程完成後再讓表單運行任何清理代碼。您現在在後臺線程和表單清理之間存在爭用條件。這個清理代碼會釋放窗口句柄,所以當後臺線程在五秒鐘後喚醒時,可能會嘗試將文本更改回UI線程,從而導致失敗。

解決此問題最簡單的方法是讓Join()任何現場後臺線程等待它們完成,然後讓表單完成關閉。更正確和更復雜的方法是設置適當的線程同步原語(Mutex, WaitHandle, Sempahore, ...)以允許您立即通知線程暫停。

+0

由於使用了Invoke/BeginInvoke,不幸的是加入到線程是不夠的。 UI線程可能會等待需要Invoke/BeginInvoke完成(將在UI線程上執行)的工作線程完成退出,從而導致死鎖,如我所驗證的。 – ceztko

0

沒有簡單的解決方案,因爲您必須與其他線程同步結束,但Invoke請求在UI線程中執行,這是應該關閉其他線程的線程!所以tUI要求t1,t2退出,但t1,t2可能需要tUI退出! :)

添加Application.DoEvents();(讀=進程的所有調用請求)quitting方法是這樣的:

private void quitting(object sender, FormClosingEventArgs e) 
    { 
     start_a = false; 
     start_b = false; 

     Application.DoEvents(); // NOT the solution, is not enough!!! 
    } 

排序大多數的比賽條件,但還遠遠不夠。

爲什麼?由於這種可能,但非常不可能的,競爭條件:

t1 before queuing Invoke 
        ~~~~~~~> 
          start_a = false; start_b= false; Application.DoEvents(); 
        <~~~~~~~ 
t1 queue an Invoke 
        ~~~~~~~> (very improbable but possible) 
          (continue trough disposing) 
        <~~~~~~~ 
queued Invoke on disposed label -> crash! 

鎖定檢驗開始變量狀態並清空消息隊列應該做的伎倆的關鍵部分。你的鍛鍊:找到其他可能的競賽條件,並找到一種方法,在最壞的情況下5秒以上退出(提示:不要使用睡眠,睡眠是惡魔)。

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 WindowsFormsApplication1 
{ 
    public partial class Form1 : Form 
    { 
     object _closing1; 
     object _closing2; 
     volatile bool start_a = false; 
     volatile bool start_b = false; 

     public Form1() 
     { 
      InitializeComponent(); 

      button1.Text = "Click to start"; 
      button2.Text = "Click to start"; 

      _closing1 = new object(); 
      _closing2 = new object(); 
     } 

     private void button1_Click(object sender, EventArgs e) 
     { 
      if (start_a == false) 
      { 
       button1.Text = "Running"; 

       start_a = true; 

       Thread thread2 = new Thread(new ThreadStart(th1)); 
       thread2.Start(); 
      } 
      else 
      { 
       button1.Text = "Click to start"; 

       start_a = false; 
      } 

     } 

     void th1() 
     { 
      int a = 0; 
      while (true) 
      { 
       lock (_closing1) 
       { 
        if (start_a == false) 
         break; 
        label1.BeginInvoke((MethodInvoker)(() => label1.Text = Convert.ToString(a))); 
       } 
       Thread.Sleep(50); 
       a++; 
      } 
     } 

     void th2() 
     { 
      int b = 0; 

      while (true) 
      { 
       lock (_closing2) 
       { 
        if (start_b == false) 
         break; 
        label2.BeginInvoke((MethodInvoker)(() => label2.Text = Convert.ToString(b))); 
       } 
       Thread.Sleep(5000); 
       b = b + 5; 
      } 
     } 

     private void button2_Click(object sender, EventArgs e) 
     { 
      if (start_b == false) 
      { 
       button2.Text = "Running"; 
       start_b = true; 
       Thread thread2 = new Thread(new ThreadStart(th2)); 

       thread2.Start(); 
      } 
      else 
      { 
       button2.Text = "Click to start"; 
       start_b = false; 
      } 
     } 

     private void Form1_FormClosing(object sender, FormClosingEventArgs e) 
     { 
      lock (_closing1) 
      { 
       start_a = false; 

       // Clear the message queue now so access on disposed lables is possible. 
       // No more invokes will be queued because 1) start_a = false 
       // 2) t1 is out of the critical section 
       Application.DoEvents(); 
      } 

      lock (_closing2) 
      { 
       start_b = false; 

       // Clear the message queue now so access on disposed lables is possible. 
       // No more invokes will be queued because 1) start_b = false 
       // 2) t2 is out of the critical section 
       Application.DoEvents(); 
      } 
     } 
    } 
} 
相關問題