0

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

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

        private static List<BlockingQueue> WorkingQueueList = new List<BlockingQueue>();
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 < WorkingQueueList.Count; x++)
        {
            /* MainThreads[x] = */ new Thread(() => MainQueueToWorkingQueue(x)).Start();
            Thread.Sleep(50);
        }

        for (Int32 x = 0; x < WorkingQueueList.Count; x++)
        {
            MainThreads[x].Start();
            Thread.Sleep(50);
        }
        Console.Read();
    }


    private static void MainQueueToWorkingQueue(Int32 WorkingQueuePosition)
    {
        /* if new Thread(() => 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());
        }
    }

我的问题是这个。为什么新建线程的时候传的参数是正确的,但是第二个for循环Start()调用的时候传的参数总是3 ?Start()

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

编辑:取出不直接处理问题的代码。

4

1 回答 1

3

您的问题是在此处使用 lambda 表达式,您在循环变量上形成闭包x而不是此时的值。结果,当线程实际启动时, 的值x可能已经改变(因为循环已经执行了进一步的迭代)并且它是传递给您的方法的新值。

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

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

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

    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

于 2013-11-14T15:10:17.873 回答