2011-03-25 80 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();