2017-04-06 9 views
-2

正常に動作するが期待どおりではない次のコードを実行すると、安全なスレッドで実行されるとみなされますが、スレッドの実行が終了するまですべてのコンポーネントがフリーズしますあなたは他のコントロールを使用することができるように新しいスレッドで実行?スレッドセーフC#が動作しない

using System; 
using System.Drawing; 
using System.Windows.Forms; 
using System.Threading; 

    public class MyFormControl : Form 
    { 
     public delegate void AddListItem(); 
     public AddListItem myDelegate; 
     private Button myButton; 
     private Thread myThread; 
     private ListBox myListBox; 
     public MyFormControl() 
     { 
     myButton = new Button(); 
     myListBox = new ListBox(); 
     myButton.Location = new Point(72, 160); 
     myButton.Size = new Size(152, 32); 
     myButton.TabIndex = 1; 
     myButton.Text = "Add items in list box"; 
     myButton.Click += new EventHandler(Button_Click); 
     myListBox.Location = new Point(48, 32); 
     myListBox.Name = "myListBox"; 
     myListBox.Size = new Size(200, 95); 
     myListBox.TabIndex = 2; 
     ClientSize = new Size(292, 273); 
     Controls.AddRange(new Control[] {myListBox,myButton}); 
     Text = " 'Control_Invoke' example"; 
     myDelegate = new AddListItem(AddListItemMethod); 
     } 
     static void Main() 
     { 
     MyFormControl myForm = new MyFormControl(); 
     myForm.ShowDialog(); 
     } 
     public void AddListItemMethod() 
     { 
     String myItem; 
     for(int i=1;i<6;i++) 
     { 
      myItem = "MyListItem" + i.ToString(); 
      myListBox.Items.Add(myItem); 
      myListBox.Update(); 
      Thread.Sleep(300); 
     } 
     } 
     private void Button_Click(object sender, EventArgs e) 
     { 
     myThread = new Thread(new ThreadStart(ThreadFunction)); 
     myThread.Start(); 
     } 
     private void ThreadFunction() 
     { 
     MyThreadClass myThreadClassObject = new MyThreadClass(this); 
     myThreadClassObject.Run(); 
     } 
    } 

// The following code assumes a 'ListBox' and a 'Button' control are added to a form, 
// containing a delegate which encapsulates a method that adds items to the listbox. 

    public class MyThreadClass 
    { 
     MyFormControl myFormControl1; 
     public MyThreadClass(MyFormControl myForm) 
     { 
     myFormControl1 = myForm; 
     } 

     public void Run() 
     { 
     // Execute the specified delegate on the thread that owns 
     // 'myFormControl1' control's underlying window handle. 
     myFormControl1.Invoke(myFormControl1.myDelegate); 
     } 
    } 
+0

ほとんどのコードは、UIスレッドで呼び出すデリゲートにあるようです。コントロールを更新する必要があるときに呼び出す必要があるのは、スリープ状態のループを実行するようなものではなく、これを 'async'にして、代わりに' Task.Delay'を使うと余分なスレッドを避けることができます。 – juharr

+0

あなた自身のコメントはそれをすべて言っています: "特定のデリゲートを 'myFormControl1'コントロールの基になるウィンドウハンドルを所有するスレッドで実行してください。そのスレッドはUIスレッドです。 – weston

答えて

2

が、それは安全なスレッドで実行すると仮定されるが、それはあなたがコントロールにいくつかのデリゲートを呼び出しているスレッド

を実行が終了するまで、すべてのコンポーネントが を凍結され、デリゲートが実行されますUIスレッド。私。このコードは、UIスレッド上で実行されます:あなたが他の のコントロールを使用できるように、新しいスレッド上で実行するように想定されていない

public void AddListItemMethod() 
    { 
    String myItem; 
    for(int i=1;i<6;i++) 
    { 
     myItem = "MyListItem" + i.ToString(); 
     myListBox.Items.Add(myItem); 
     myListBox.Update(); 
     Thread.Sleep(300); // freeze UI thread 
    } 
    } 

UI以外のスレッドのコントロールは使用できません。

バックグラウンドスレッドを使用する目的は、UIに関係しない長期実行の操作です。例えば。ディスクからいくつかのファイルを読み込むこともできますし、query apiを使うか、長時間実行される計算(n番目のフィボナッチ数)を実行することもできます。 UIスレッドでこれらの種類のものを実行すると、アプリケーションがフリーズします。したがって、UI以外のスレッドでこのような操作を実行し、の実行後にUI に戻る必要があります(長期実行操作の進行状況をユーザーに通知することはできますが)。

UIで定期的に何かを実行する場合は、System.Windows.Forms.Timerコンポーネントを使用することを検討してください。 300にタイマ間隔を設定し、Tickイベントハンドラを追加します。

private void Button_Click(object sender, EventArgs e) 
{ 
    timer.Start(); 
} 

private void Timer_Tick(object sender, EventArgs e) 
{ 
    myListBox.Items.Add($"MyListItem{myListBox.Items.Count + 1}"); 
} 
+0

ありがとう私はそれを試してみます –

0

問題は、あなただけのだけのすべての作業を行うためにUIスレッドを伝えるスレッドを作成しているのでInvokeは、UIスレッド上で、デリゲートを実行していることです。代わりにasyncawaitTask.Delayを使用すると、コードを簡略化できます。 UI上のコードを実行されますが、今await Task.Delay(300)が、それは他のUIイベントを処理することが可能と凍結の問題を停止するUIスレッドをブロックすることはありません

private async void Button_Click(object sender, EventArgs e) 
{ 
    String myItem; 
    for(int i=1;i<6;i++) 
    { 
     myItem = "MyListItem" + i.ToString(); 
     myListBox.Items.Add(myItem); 
     myListBox.Update(); 
     await Task.Delay(300); 
    } 
} 

+0

それはちょうどうまく動作しますありがとう!、私はどのように知っているが、それはスレッドのような仕事を想定?私はバックグラウンドでいくつかのことをするためのボタンをクリックし、私はまだ問題なくメインのUIで作業している場合、別のクラスをインスタンス化することはできますか? –

+0

基本的に、このコードは非同期状態マシンにコンパイルされます。それはそれぞれの 'await'でコードの実行を分割し、300ミリ秒の間にUIスレッドは他のものを自由に行うことができます。 – juharr

+0

私は、{myListBox.Items.Add(myItem);}行がUIを終了するのに約1分かかる場合は、何とかフリーズしますか? –

関連する問題