2011-02-08 40 views
1

类似的问题被问到here,但答案一般都与lambda表示法有关。我得到没有拉姆达类似的结果,所以我认为我会问一些澄清:线程启动的竞态条件

说我有这样的事情:

for (int i = 0; i < 5; i++) 
    (new Thread(new ThreadStart(delegate() 
    { 
     Console.WriteLine("Thread " + i); 
    }))).Start(); 

人们期望的输出如下:

Thread 0 
Thread 1 
Thread 2 
Thread 3 
Thread 4 

现在我意识到线程并不是以任何特定的顺序开始的,所以我们假设上面的行可以以任何顺序出现。

但这不是发生了什么。 什么,而不是发生:

Thread 3 
Thread 4 
Thread 4 
Thread 4 
Thread 4 

或类似的东西,这使我相信,而不是传递价值,如果我,它是通过基准。 (这很奇怪,因为int是一个值类型)。

做这样的事情:

for (int i = 0; i < 5; i++) 
    (new Thread(new ThreadStart(delegate() 
    { 
     int j = i; 
     Console.WriteLine("Thread " + j); 
    }))).Start(); 

并没有帮助,虽然我们已经取得了我的副本。我假设原因是它没有及时提供我的副本。

做这样的事情:

for (int i = 0; i < 5; i++) 
{ 
    (new Thread(new ThreadStart(delegate() 
    { 
     Console.WriteLine("Thread " + i); 
    }))).Start(); 
    Thread.Sleep(50); 
} 

似乎解决这个问题,但它是非常不可取的,因为我们在每次迭代50ms的浪费,更何况一个事实,即如果计算机负载过重然后也许50ms可能是不够的。

这里是我当前的特定问题的例子:

Thread t = new Thread(new ThreadStart(delgate() 
    { 
    threadLogic(param1, param2, param3, param4); 
    })); 
t.Start(); 

param1 = param2 = param3 = param4 = null; 

有:

void threadLogic(object param1, object param2, object param3, object param4) 
{ 
    // Do some stuff here... 
} 

我想threadLogic()在它自己的线程中运行,但上面的代码给出了一个空参考例外。我认为这是因为在线程有机会开始之前将值设置为null。

再次,把Thread.Sleep(100)的作品,但它是从各个方面的可怕的解决方案。 你们对这种特殊类型的比赛状况有何建议?

+0

见http://stackoverflow.com/questions/1930133/c-closures-why-is-the-loopvariable-captured-by-reference和http://stackoverflow.com/问题/ 1923577 /不同行为时,什么时候开始线程参数化线程启动vs匿名/ – nos 2011-02-08 23:31:41

回答

3

您需要引入一个临时的:

for (int i = 0; i < 5; i++) 
{ 
    int temp = i; // Add this 
    (new Thread(new ThreadStart(delegate() 
    { 
     Console.WriteLine("Thread " + temp); 
    }))).Start(); 
} 

的问题是在代表紧密围绕外部变量(i在你的代码,temp矿)如何。范围是错误的(在for循环之外),所以在线程启动时,如果不是全部,i已经增加了大部分。


对于第二个例子,你需要做同样的事情。只是让临时工:

var temp1 = param1; 
var temp2 = param2; 
var temp3 = param3; 
var temp4 = param4; 
Thread t = new Thread(new ThreadStart(delgate() 
    { 
    threadLogic(temp1, temp2, temp3, temp4); 
    })); 
t.Start(); 

// This is now safe, since the closure above is over "temp*" 
param1 = param2 = param3 = param4 = null; 
+0

谢谢,这是我给出的简单示例的一个很好的解决方案。在t.Start()之后立即将线程参数设置为null的另一个问题呢? – Ozzah 2011-02-08 23:40:21

3

你的问题是一样的;它不是lambda语法本身,而是通过匿名方法关闭局部变量(您使用的delegate语法是匿名方法的第一次迭代,并在.NET 2.0中首次亮相)。

如果你想做到这一点,你必须使用一种解决方法:

for (int i = 0; i < 5; i++) 
{ 
    int j = i; 
    (new Thread(new ThreadStart(delegate() 
    { 
     Console.WriteLine("Thread " + j); 
    }))).Start(); 
} 

注意,这类似于你尝试过什么(复制),但它需要以外的关闭和里面的循环。在匿名函数中复制它(例如在你的例子中)并没有帮助。