2012-08-13 20 views
1

在我的代码下面,ThreadClassId属性未按预期确定性设置(ThreadArray[0]'s ThreadClass.Id = 0, ThreadArray[1]'s ThreadClass.Id = 1,等)。并发传递值类型为'Thread.Start'不能按预期方式工作

如果我调试并放慢了Thread.Start()'s,一切都按预期工作。但是当程序全速运行时,我得到所有Id's = 4(或类似的)。我无法锁定i,因为它不是参考变量。显然,我遇到了一个竞争条件。我究竟做错了什么?

Main.cs

for (int i = 0; i < ThreadCount; i++) 
{ 
    ThreadArray[i] = new Thread(() => 
     { 
      new ThreadClass(i); 
     }); 
    ThreadArray[i].Start(); 
} 

ThreadClass.cs

private int Id { get; set; } 

public ThreadClass(int i) { 
    Id = id; 
    while(true) 
    { 
     Console.WriteLine("I am thread " + i"); 
     Thread.Sleep(5000); 
    } 
} 

预期输出:

I am thread 0 
I am thread 1 
I am thread 2 
I am thread 3 
... 5 second wait ... 
I am thread 0 
I am thread 1 
I am thread 2 
I am thread 3 

实际输出:

I am thread 4 
I am thread 4 
I am thread 4 
I am thread 4 
... 5 second wait ... 
I am thread 4 
I am thread 4 
I am thread 4 
I am thread 4 

注意,在这一点上的ThreadArray每个实例初始化为有效Thread对象。

回答

2

您使用一个版本的C#不收,较i变量的新副本。线程执行时,iThreadCount处或附近。只需创建变量的副本,以便关闭可以捕捉替代:

for (int i = 0; i < ThreadCount; i++) 
{ 
    int temp = i; 
    ThreadArray[i] = new Thread(() => 
     {    
      new ThreadClass(temp); 
     });  
} 

顺便说一句,或C#中VS 2012(或者.NET 4.5)版本修复了这个。请参阅http://msdn.microsoft.com/en-us/library/hh678682(v=vs.110).aspx

+2

有关详细信息,请参阅http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx – 2012-08-13 20:33:00

+1

对于未来的读者,基于Rob的博客文章,.Net 4.5不会改变上述情况下的行为。这个改变只影响foreach()语句,如果你希望lambda访问一个循环变量,你仍然需要将循环变量复制到一个局部变量。 – 2012-08-13 20:55:32

-1

将您的ThreadArray [i] .Start()移动到for循环之外,并在启动线程之前设置所有类。即使你将不得不循环两次,它应该确保所有事情都发生在你想要的顺序/状态中。

--edit Darnit得到的答案就在我did-- :)

相关问题