2017-08-05 3 views
1

Ich arbeite ein winForm Projekt, fügte ich eine listBox in Form, als listBox1 benannt. Der Code ist wie folgt:Task manchmal starten, manchmal nicht, warum? Wie man es ändert?

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, beginnt manchmal nicht, warum? wie man ändert? Danke!

Betriebsumgebung: Microsoft Windows 10, .NET4.5.1

Es gibt ein Problem: wenn zwischen T1 und T2 sowie einem MessageBox.Show ("some string"); das Programm kann auch richtig funktionieren, ist das der Grund?

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); 

       } 
      } 

     } 

    }); 

Antwort

0

Die Planung der Aufgaben wird durch die TaskScheduler behandelt und es gibt keine Garantie dass t1 für Ausführung eingeplant werden, bevort2

Da beide des Lambda-Ausdrücke, die Sie Action zu definieren verwenden, um s, die in t1 und t2über die gleiche Variable schließeninputMax laufen Sie wahrscheinlich in eine :

  1. inputMax ist standardmäßig == 0 default(int)
  2. t2 beginnt mit der Ausführung erste und inputMax==0
  3. in t2 der Zustand, in dem während falsch und die Ausführung ist beendet

einige hinzufügen Protokollierung, um diese Situation für sich selbst zu sehen

Es auch scheint, dass Ihre Programmlogik etwas fehlerhaft ist, weil es aussieht wie t2 kann die Anzahl der Elemente schneller erschöpfen als t1 wird in der Lage sein, sie zu produzieren (abhängig von den Schlafzeiten, die Sie verwenden).

Sofern Sie nicht ausdrücklich in eine Race-Bedingung ausgeführt werden sollen, ich glaube, Sie Task.Run für t1 und Task.ContinueWith für ‚t2‘

Einen letzten Punkt verwenden sollen, auch wenn Sie interlocked verwenden von verschiedenen Threads zu setzen inputMax, auf eine thread-sichere Weise - immer noch mit ein Zähler in einem Zustand ist keine gute Synchronisationsstrategie.

Wenn Sie noch Threads über einen gemeinsam genutzten Zustand koordinieren müssen (nicht der beste Weg) - Sie einen Zähler vor t1 und t2 Start berechnen kann - und den Wert für beide unabhängig Aufgaben übergeben, und nutzen des Wartens in irgendeiner Form (Schlaf/Spining), im Geiste der producer-consumer mit blockierenden Consumer-Teil - weil t2 muss wissen, wann nicht mehr warten auf Eingabe/Nachrichten.

Die anderen Optionen könnten sein - Ausführen der Aufgaben der Reihe nach (mit ContinueWith) oder Thread-Signalisierungstechnik mit einer der Unterklassen EventWaitHandle.

+0

Berechnen Sie inputMax vor t1 und t2, das Programm kann ordnungsgemäß funktionieren. Würdest du bitte erklären warum? Durch Ihre Anleitung habe ich viel gelernt und werde meine Bemühungen fortsetzen. vielen Dank! –

+0

@Backbone_Moutain - weil 't2' irgendwie wissen muss ** wann man keine neuen Eingaben/Nachrichten erwartet **. Das Übergeben einer Zählung und das Verwenden von Schlafen/Warten ist der einfachste Weg, dies zu tun. – ironstone13

+0

Ich verstehe, bitte empfehlen Sie mehr Wissen über Multithreading-Sites. Danke –

0

Ein Problem, das ich denken kann ist, dass die inputMax Variable in beiden Aufgaben verwendet und nicht Thread-Safe ist.

Sie sollten die Klasse Interlocked verwenden, damit sich dies korrekt verhält. Weitere Informationen zur Klasse here.

+0

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

Verwandte Themen