线程启动的竞争条件

时间:2011-02-08 23:24:04

标签: c# multithreading race-condition

类似的问题被问到here,但答案通常都与lambda符号有关。没有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();

也没有帮助,即使我们已经复制了i。我假设原因是它没有及时复制我。

做这样的事情:

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)设置为有效,但从各个方面来看都是一个糟糕的解决方案。 对于这种特殊类型的竞争条件,你们有什么建议?

2 个答案:

答案 0 :(得分: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();
}

请注意,这与您尝试(复制)的内容类似,但它需要在闭包的内部循环。在匿名函数中复制它(如在您的示例中)并没有帮助。

答案 1 :(得分: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;