2013-09-27 44 views
2

我試圖實現的是在集合中的每個值的不同線程中執行一個方法。我想測量所有任務完成前的時間。我的代碼如下:System.Threading.Tasks.Task在循環中執行的奇怪行爲

private ConcurrentQueue<string> Results { get; set; } 
public System.Threading.Timer Updater { get; set; } 
private Dictionary<int, string> Instances { get; set; } 

     private void Button_Click(object sender, RoutedEventArgs e) 
     { 
      this.Instances = new Dictionary<int, string>(); 

      this.Instances.Add(01, "A"); 
      this.Instances.Add(02, "B"); 
      this.Instances.Add(03, "C"); 
      this.Instances.Add(04, "D"); 
      this.Instances.Add(05, "E"); 
      this.Instances.Add(06, "F"); 
      this.Instances.Add(07, "G"); 
      this.Instances.Add(08, "H"); 
      this.Instances.Add(09, "I"); 
      this.Instances.Add(10, "J"); 

      this.Updater = 
      new System.Threading.Timer(new TimerCallback(Updater_CallBack), null, 0, 1000); 
     } 

     /// <summary> 
     /// 
     /// </summary> 
     /// <param name="State"></param> 
     void Updater_CallBack(object State) 
     { 
      this.Operation(); 
     } 

     private void Operation() 
     { 
      var Watcher = new System.Diagnostics.Stopwatch(); 
      Watcher.Restart(); 

      this.Results = new ConcurrentQueue<string>(); 

      var Tasks = new System.Threading.Tasks.Task[this.Instances.Count]; 

      int i = 0; 

      foreach (var Pair in this.Instances) 
      { 
       Tasks[i] = Task.Factory.StartNew(() => { this.Tasker(Pair.Value); }); 
       i++; 
      } 

      System.Threading.Tasks.Task.WaitAll(Tasks); 

      Watcher.Stop(); 

      var Text = new StringBuilder(); 
      foreach (var Result in Results) Text.Append(Result); 
      System.IO.File.AppendAllText(@"D:\Tasks.TXT", Watcher.ElapsedMilliseconds.ToString() + " " + Text.ToString() + Environment.NewLine);   
     } 

     private void Tasker(string Id) 
     { 
      switch (Id) 
      { 
       case "A": Thread.Sleep(100); this.Results.Enqueue("a"); break; 
       case "B": Thread.Sleep(100); this.Results.Enqueue("b"); break; 
       case "C": Thread.Sleep(100); this.Results.Enqueue("c"); break; 
       case "D": Thread.Sleep(100); this.Results.Enqueue("d"); break; 
       case "E": Thread.Sleep(100); this.Results.Enqueue("e"); break; 
       case "F": Thread.Sleep(100); this.Results.Enqueue("f"); break; 
       case "G": Thread.Sleep(100); this.Results.Enqueue("g"); break; 
       case "H": Thread.Sleep(100); this.Results.Enqueue("h"); break; 
       case "I": Thread.Sleep(100); this.Results.Enqueue("i"); break; 
       case "J": Thread.Sleep(100); this.Results.Enqueue("j"); break; 
      } 
     } 

我希望這裏是每次不同的結果塔斯克方法10倍的執行,但。當我看看我的結果時,我看到以下內容:

200 jjjjjjjjjj 
101 jjjjjjgjjj 
101 hhhhhhhhjj 
101 hjjjjjjjjj 
101 jjjjjjjjhj 
100 jjjjjjjjjh 
101 jjjjjjjjjj 

Tasker方法多次執行'j'。我無法弄清楚爲什麼該方法沒有爲集合中的其他字母執行。我在這裏錯過了什麼?

+1

這是「關閉錯誤」,在這個網站上有很多很多帖子。見[這個問題](http://stackoverflow.com/questions/8898925/is-there-a-reason-for-cs-reuse-of-the-variable-in-a-foreach/8899347) –

+0

我wouldn'噸稱它是一個錯誤。我會用'Tasks = this.Instances.Select(x =>(Action)(()=> this.Tasker(x.Value)))將foreach切換出來,選擇(x => Task.Factory.StartNew X))。ToArray的()'。這就像C#一樣可以編譯和運行的僞代碼。 – Will

+0

@Will這是OP代碼中的一個錯誤,他無意中關閉了正在修改的變量。 – Servy

回答

1

我認爲本質上問題在於您正在訪問從傳遞給Task.Factory.StartNew的操作中的Pair變量。

foreach (var Pair in this.Instances) 
{ 
    Tasks[i] = Task.Factory.StartNew(() => { this.Tasker(Pair.Value); }); 
    i++; 
} 

你必須認識到,這意味着你是不是讀變量「配對」向右走,你告訴了任務工廠排隊線程池線程。然後,當線程開始時(可能在不確定的時間間隔後發生),您正在訪問該線程本地範圍內的一個變量,該變量已經更改,因爲foreach循環沒有等待任務啓動。

你能解決這個問題是這樣的:

foreach (var Pair in this.Instances) 
{ 
    var currentValue = Pair.Value; 
    Tasks[i] = Task.Factory.StartNew(() => { this.Tasker(currentValue); }); 
    i++; 
} 

現在你正在做一個獨立的局部範圍的變量不應該在你改變的foreach的每次迭代。另一方面,我認爲你的代碼的一個單獨的邏輯問題是你重複使用每次外循環的ConcurrentQueue引用,但是你每次都改變它。你不應該有這條​​線:

private ConcurrentQueue<string> Results { get; set; } 

你應該使用一些更好的方式來管理每個線程組的結果。在這種情況下,您可以使用ConcurrentQueues的ConcurrentQueue ...但我個人會使用任務參數。在您的Operation方法中本地創建ConcurrentQueue,並將其作爲輔助參數傳遞給Tasker方法。

+0

謝謝@Trevor Elliott提供的信息。問題現在很清楚。順便說一下,這是一個簡單的測試代碼,以瞭解我的原始代碼中的問題。原始代碼不需要任何返回值。無論如何感謝您的建議。 – Demir