如果单独调用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
- 从IDbCommandInterceptor的实现中获取DbContext
- 当VirtualizingStackPanel.IsVirtualizing =“False”时,ItemContainerGenerator.ContainerFromItem()返回null
- DataGridView Masked TextBox列
- HRESULT:0x8004D00E使用TransactionScope – C#
- 使用动态类型调用generics扩展方法
- 清除控制台缓冲区
- ASP.NET成员资格提供程序 – 重置密码function – 电子邮件确认和密码更改
- 读取文件的最后30,000行
- 在webapi中使用OData来获取仅在运行时知道的属性