开始在foreach循环

开始在foreach循环

问题描述:

一个新的线程我有对象的名单,我想遍历该列表,并开始一个新的线程,传递当前对象。开始在foreach循环

我已经写了什么,我觉得应该这样做的例子,但它不工作。具体来说,似乎线程在每次迭代中都被覆盖。这并没有真正意义的我,但因为我每次做一个新的Thread对象。

这是测试代码,我写

class Program 
{ 
    static void Main(string[] args) 
    { 
     TestClass t = new TestClass(); 
     t.ThreadingMethod(); 
    } 
} 

class TestClass 
{ 
    public void ThreadingMethod() 
    { 
     var myList = new List<MyClass> { new MyClass("test1"), new MyClass("test2") }; 

     foreach(MyClass myObj in myList) 
     { 
      Thread myThread = new Thread(() => this.MyMethod(myObj)); 
      myThread.Start(); 
     } 
    } 

    public void MyMethod(MyClass myObj) { Console.WriteLine(myObj.prop1); } 
} 

class MyClass 
{ 
    public string prop1 { get; set; } 

    public MyClass(string input) { this.prop1 = input; } 
} 

我的机器上输出是

test2 
test2 

,但我希望它是

test1 
test2 

我试图改变螺纹线到

ThreadPool.QueueUserWorkItem(x => this.MyMethod(myObj)); 

但没有一个线程的开始。

我想我只是有一个关于线程应该如何工作的误解。有人能指出我正确的方向并告诉我我做错了什么吗?

+1

你的生活会所以容易得多,如果你检查出的并行扩展库中的.Net 3.5推出。这里有一个地方开始:http://msdn.microsoft.com/en-us/library/dd460693%28VS.100%29.aspx – DOK 2012-02-23 18:04:59

+0

http://www.albahari.com/threading/ – 2017-10-31 06:28:42

这是因为你关闭了一个变量在错误的范围。这里的解决方案是使用临时在您的foreach循环:

foreach(MyClass myObj in myList) 
    { 
     MyClass tmp = myObj; // Make temporary 
     Thread myThread = new Thread(() => this.MyMethod(tmp)); 
     myThread.Start(); 
    } 

有关详细信息,我建议你阅读关于这个确切的主题埃里克利珀的帖子:Closing over the loop variable considered harmful

+1

你打我吧! – 2012-02-23 18:04:45

+0

这是一个快速反应。哇。 – BlueM 2012-02-23 18:06:00

+1

嘎,我踢了自己,因为我实际上阅读[这个问题](http://*.com/q/8898925/817630)上个月是答案。我甚至看过Eric的博客文章。谢谢你指出我应该记得我自己。 – 2012-02-23 18:18:48

的问题是,您使用的是最新的值你的封闭内部的对象。所以,线程的每次调用都会看到相同的值。要解决此问题,请将该值复制到局部变量中:

foreach(MyClass myObj in myList) 
{ 
    MyClass localCopy = myObj; 
    Thread myThread = new Thread(() => this.MyMethod(localCopy)); 
    myThread.Start(); 
} 

同意里德的回答(+1)。

我想补充一点,如果你是.NET 4,你可能想看看任务并行库来解决这一类的问题。专门针对这种情况,看看Parallel.ForEach()

+1

虽然我喜欢Parallel.ForEach,但它意识到它本身就是一种阻塞方法,OP的“火和遗忘” - 因此使用它有一个功能差异。 – 2012-02-23 18:09:45

如果顺序没有关系比去

Parallel.ForEach(myList, obj => this.MyMethod(obj)); 

Write a Simple Parallel.ForEach Loop

+0

Parallel.ForEach本身是一种阻塞方法,其中OP的“火灾和遗忘” - 因此使用它有一个功能差异。在这种情况下,这可能不合适(至少不是没有将它放入任务本身) – 2012-02-23 18:14:04

我更喜欢这种方式:

public void ThreadingMethod() 
{ 
    var myList = new List<MyClass> { new MyClass("test1"), new MyClass("test2") }; 


Parallel.ForEach(myList, new ParallelOptions() { MaxDegreeOfParallelism = 100 }, 
     (myObj, i, j) => 
     { 
      MyMethod(myObj); 
     }); 

} 

虽然没有测试....

+0

Parallel.ForEach具有与OP方法不同的功能行为。原始代码具有“火灾和遗忘”行为,而这将阻止调用线程,直到操作完成。另外,我不建议设置MaxDegreeOfParallelism,除非有特定的理由这样做。 – 2012-02-23 19:17:07

+0

@ReedCopsey - 我想你只是假设。使用Parallel.ForEach始终是在您的应用程序中实现PROPER线程的首选方法,尤其是当您有大量要处理的项目(同时)并且没有足够的资源来使用简单的Thread()时。如果他用你建议的方法使用1000个线程,那么它的实现非常糟糕。 – SolidSnake 2012-02-23 19:52:06

+0

Parallel.ForEach并不总是首选的方法。这是一个很好的选择,但是其中之一。使用任务或ThreadPool通常比自己管理线程更好,但即使如此,有时直接使用Thread仍是首选。 OP代码中没有足够的信息来暗示Parallel.ForEach将是首选。 (虽然我几乎总是选择一个长时间运行的Task,而不是手动管理的Thread) – 2012-02-23 20:12:48