如果单独调用start(),则线程改变传递Int

我已经阅读了有关线程的一些内容,并知道我应该锁定一个由多个线程访问的变量。 我遇到的问题是,如果我通过首先分配线程启动线程,然后在第二个for循环中调用start,则MainQueueToWorkingQueue(x)值始终为3。

但是,如果我这样做new Thread(() => MainQueueToWorkingQueue(x)).Start(); 然后代码按预期运行并传递正确的数字。

  private static List WorkingQueueList = new List(); static void Main(string[] args) { for(Int32 WorkingQueueListInsers = 0; WorkingQueueListInsert < 3; WorkinfQueueListInsert++) { WorkingQueueList.Add(new BlockingQueue(20)); } Thread[] MainThreads = new Thread[WorkingQueueList.Count]; for (Int32 x = 0; x  MainQueueToWorkingQueue(x)).Start(); Thread.Sleep(50); } for (Int32 x = 0; x  MainQueueToWorkingQueue(x)).Start(); is called then WorkingQueuePosition is correct and is either zero, one, or two. */ /* if the thread is allocated in one for loop, then started in another for loop, WorkingQueuePosition only equals three */ Console.WriteLine("Ending x: " + WorkingQueuePosition); while (true) { WorkingQueueList[WorkingQueuePosition].Enqueue(MainQueue.Dequeue()); } } 

我的问题是这个。 当我在创建新线程时使用Start()时,为什么传递的参数是正确的,但是当我在第二个for循环中调用Start()时传递的参数总是三个?

我的猜测:我知道某个参数正在改变。 我的第一个猜测是循环运行得很快,因为线程使用的值与传递的值不同,因为x更新得太快了。 我尝试使用Thread.Sleep(50)修复它,但问题仍然存在。

编辑:找出没有直接处理问题的代码。

你的问题在于使用lambda表达式,在这里你要在循环变量x上形成一个闭包, 而不是在那个点上形成一个闭包。 因此,当线程实际启动时, x的值可能已更改(因为循环已执行进一步的迭代),并且它是传递给方法的新值。

如果您使用的是ReSharper,它会警告您“访问修改后的闭包”。

请注意,您的“工作”版本仍然容易出现问题,但随着线程在循环中启动, x的值不会发生变化的可能性更大(特别是在那里睡眠时间为50ms)。 但是,如果其中一个线程启动时间超过50毫秒,您仍会看到错误的值。

您可以通过将x的值复制到循环内的局部变量来解决此问题。 这将在两种情况下修复代码 – 无论是在此循环中启动线程,还是将线程存储到MainThreads / WorkingThreads数组中并在以后启动它们。 你的50ms睡眠也不再需要了。

  for (Int32 x = 0; x < WorkingQueueList.Count; x++) { var localX = x; Console.WriteLine("Starting x: " + x); /* MainThreads[x] = */ new Thread(() => MainQueueToWorkingQueue(localX)).Start(); /* WorkingThreads[x] =*/ new Thread(() => WorkingQueueToJob(localX)).Start(); } 

您可以在此处阅读有关此问题的更多信息: http : //blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx