2017-08-05 23 views
1

winFormプロジェクトを進めていますが、listBox1という名前のlistBoxをフォームに追加しました。 次のようにコードは次のとおりです。タスクが開始することがありますが、時にはそれはなぜですか?それを変更する方法はありますか?

private int inputMax; 
private void button1_Click(object sender, EventArgs e) 
{ 
    Task t1 = Task.Run(() => 
    { 
     string[] input = { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "end" }; 
     inputMax = input.Length; 
     foreach (string s in input) 
     { 
      Thread.Sleep(new Random().Next(1000, 2001)); 
      if (listBox1.InvokeRequired) 
      { 
       listBox1.Invoke(new Action(() => listBox1.Items.Add(s))); 
      } 
     } 

    }); 

    Task t2 = Task.Run(() => //t2 sometimes not start 
    { 
     while (inputMax > 0) 
     { 
      Thread.Sleep(2000); 
      if (listBox1.InvokeRequired) 
      { 
       if ((int)listBox1.Invoke(new Func<int>(() => listBox1.Items.Count)) > 0) 
       { 
        listBox1.Invoke(new Action(() => listBox1.Items.RemoveAt(0))); 
        inputMax--; 
       } 
      } 

     } 

    }); 

} 

T2、時々起動していないが、なぜですか?変更する方法はありますか?ありがとうございます!

動作環境:問題があり.NET4.5.1

windows10、次の場合にT1とT2、プラスMessageBox.Show( "一部の文字列")の間。プログラムは正常に動作することができます、これはなぜですか?

Task t1 = Task.Run(() => 
    { 
     string[] input = { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "end" }; 
     Interlocked.Exchange(ref inputMax, input.Length); 
     foreach (string s in input) 
     { 
      createLog(@"F:\tasklog.txt", "t1---" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "---inputMax:"+inputMax.ToString()+ "\r\n"); 
      Thread.Sleep(new Random().Next(1000, 2001)); 
      if(listBox1.InvokeRequired) 
      { 
       listBox1.Invoke(new Action(() => listBox1.Items.Add(s))); 
      } 
     } 

    }); 

MessageBox.Show("some string"); //Add this,the progaram can work properly,why? 

Task t2 = Task.Run(() => 
    { 
     while(inputMax>0) 
     { 
      createLog(@"F:\tasklog.txt", "t2---" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "---inputMax:"+inputMax.ToString()+"\r\n"); 
      Thread.Sleep(2000); 
      if(listBox1.InvokeRequired) 
      { 
       if ((int)listBox1.Invoke(new Func<int>(() => listBox1.Items.Count)) > 0) 
       { 
        listBox1.Invoke(new Action(() => listBox1.Items.RemoveAt(0))); 
        Interlocked.Decrement(ref inputMax); 

       } 
      } 

     } 

    }); 

答えて

0

タスクのスケジューリングはTaskSchedulerによって処理され、t1Actionを定義するために使用するラムダの両方以来t2

前に実行のためにスケジュールされることを保証はありません秒で実行されますt1t2同じ変数を閉じるinputMaxあなたはおそらくを実行していますrace condition

  1. inputMaxしばらくデフォルト==で0 default(int)
  2. t2最初の実行を開始し、t2inputMax==0
  3. 条件では偽であり、実行が

いくつかの追加を停止し、この状況を自分で確認するためのログ記録

t2は、t1が(使用しているスリープ時間に応じて)生成できるようになるとすぐに項目の数を使い切ることができるように見えるため、プログラムのロジックに多少の欠陥があるようです。

あなたは、具体的競合状態に実行したい場合を除き、私はあなたが別のスレッドからinputMaxを設定するためにinterlockedを使用している場合でも、最後の注意の「T2」

ためt1Task.ContinueWithためTask.Runを使用すべきだと思います、スレッドセーフな方法で - まだカウンターを使用しても良い同期化戦略ではありません。

それでも共有状態(ない最良の方法)を経由してスレッドを調整する必要がある場合 - あなたはt1t2開始前にカウンターを計算することができます - と独立して両方のタスクに値を渡すと、(待機中のいくつかのフォームを利用睡眠/脊髄)、producer-consumerの精神で、ブロックする消費者部分 - t2は、を待つのを止めるときは、を知らなければならないので。

他のオプションは、タスクを順番に(ContinueWithを使用して)実行するか、EventWaitHandleサブクラスのいずれかでスレッドシグナリング手法を使用することです。

+0

t1とt2の前にinputMaxを計算すると、プログラムは正常に動作します。理由を説明してください?あなたの指導によって、私は多くを学び、私の努力を続けます。どうもありがとうございました! –

+0

@Backbone_Moutain - 't2'は何らかの理由で新しい入力/メッセージを待つことを知らなくてはなりません**。 カウントを渡し、睡眠/待機を使用するのが最も簡単な方法です。 – ironstone13

+0

私は、マルチスレッドサイトについてのより多くの知識をお勧めします。ありがとうございました –

0

私が考えることができる1つの問題は、inputMax変数が両方のタスクで使用されていて、スレッドセーフではないということです。

正しく動作するには、Interlockedクラスを使用する必要があります。クラスhereの詳細。

+0

inputMax = input.Length; ---> Interlocked.Exchange ref inputMax、input.Length); inputMax - ; ---> Interlocked.Decrement(ref inputMax); –

関連する問題