2011-07-09 75 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に設定しますが、フォームがクリーンアップコードを実行する前にバックグラウンドスレッドが終了するまで待機しません。これで、バックグラウンドスレッドとフォームクリーンアップの間に競合状態が発生しました。このクリーンアップコードはウィンドウハンドルを解放するので、バックグラウンドスレッドが5秒後に起動し、テキストの変更をUIスレッドに呼び戻そうとしたときにエラーが発生します。

問題を解決する最も簡単な方法は、ライブのバックグラウンドスレッドをJoin()にして、フォームが終了する前に終了するのを待つことです。より正確でより複雑な方法は、適切なスレッド同期プリミティブ(Mutex, WaitHandle, Sempahore, ...)をセットアップして、スレッドに直ちに停止を知らせることができます。

+0

残念ながら、Invoke/BeginInvokeの使用のために残念ながらスレッドへの参加は不十分です。 UIスレッドは、検証したとおり、デッドロックの原因となるInvoke/BeginInvoke完了(UIスレッドで実行される)の完了を必要とするワーカースレッドの完了を待機することがあります。 – ceztko

0

他のスレッドと同期する必要があるため簡単な解決策はありませんが、InvokeはUIスレッドで実行を依頼します。したがって、tUIはt1、t2に終了を依頼しますが、t1、t2はtUIを終了する必要があります。 :)

は、このようなquitting方法に(すべてがリクエストを呼び出す=プロセスを読む)Application.DoEvents();の追加:

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(); 
      } 
     } 
    } 
} 
関連する問題