2011-03-25 168 views
1

我想了解LINQ如何處理線程。以下代碼會生成ThreadStateException「線程尚未啓動」。爲什麼會發生?LINQ枚舉線程

 var threads = Enumerable.Range(0, 50).Select(x => 
                 { 
                  Thread thread = new Thread(Method); 
                  thread.Name = x.ToString(); 
                  return thread; 
                 }); 


     foreach (var thread in threads) 
     { 
      thread.Start(); 
     } 

     foreach (var thread in threads) 
     { 
      thread.Join(); 
     } 

     Console.WriteLine(j); 

回答

9

你的問題是無關的線程。枚舉threads第二次執行第二次查詢並創建第二組線程。

所以你.Start一組線程,.Join另一個。您需要熱切評估一次查詢,然後緩存結果。

IEnumerable<Thread> lazyThreads = Enumerable.Range(... 
Thread[] threads=lazyThreads.ToArray();//Evaluate and store in an array 

你也可以把它寫成一個單獨的語句:

var threads = Enumerable.Range(1,50).Select(...).ToArray(); 
4

threads變量是查詢,不是線程的列表。每次迭代它時,都會再次執行查詢,因此在第二種情況下,您在不致致電Start()的情況下致電Join()

您可以在查詢的末尾添加ToList()使它列表:

var threads = Enumerable.Range(0, 50).Select(x =>{ ... }).ToList(); 
2

你兩次枚舉lambda表達式(懶惰枚舉) - 因此你的第二個的foreach是通過不同於第一線迭代。

嘗試

var threads = Enumerable.Range(0, 50).Select(...).ToList(); 

foreach(var thread in threads) 
... 

調用ToList()將創造超過您可以隨後列舉多次線程的列表。

1

在這種情況下,你並沒有真正探索LINQ如何處理線程。相反,您正在使用鏈接來保存幾行代碼,同時處理自己的線程。

從.Net 4.0開始,LINQ可以開箱即用地完成一些簡潔的線程幫助。調查Parallel LINQ。還請查看Parallel Extensions

至於你老式的線程處理,你碰巧正在使用LINQ,你的問題實際上是由你使用LINQ引起的。你的問題與delayed execution有關。在調試模式下,在該行完成後,將線程懸停。你會看到它是一個查詢,而不是一個簡單的集合。每次訪問這個變量時,它都會重新獲得它的值。

修復很容易,也很常見:只需在第一行的末尾添加.ToList()。您的隱含類型線程變量將成爲一個簡單的列表,並按照您的預期運行。

var threads = Enumerable.Range(0, 50).Select(x => 
           { 
            Thread thread = new Thread(Method); 
            thread.Name = x.ToString(); 
            return thread; 
           }).ToList();