正在将重复计数器传递给for循环中的线程

时间:2015-05-21 10:06:02

标签: c# multithreading

这是实际问题的示例代码

//Dictionary to hold unique keys
static Dictionary<int, int> list = new Dictionary<int, int>();

//Worker
static void Do(int index)
{
    list.Add(index, index);
}

static void Main(string[] args)
{
    int counter = 1000;
    for (int i = 0; i < counter; i++)
    {
        int index = i;
        System.Threading.Thread t = new System.Threading.Thread(
                () => Do(index));
        t.Start();
    }
}

问题: list并不总是包含1000个元素,因为传递给index的{​​{1}}正在重复,并且线程正在崩溃。

我认为由于以下情况而出现问题

    值为0的
  1. Do(index)将传递给第一个线程
  2. 第一个线程的index被调用,但线程实际上并不是 开始了
  3. 在下一次迭代中,Start()变为1
  4. 现在第一个线程启动并接收index为1
  5. 创建第二个帖子
  6. 调用第二个线程index,线程接收Start()为1
  7. 现在两个线程都试图添加1作为键
  8. 其中一人崩溃
  9. index的计数是1
  10. 这是在这种情况下发生的事情吗?解决方案是什么?

2 个答案:

答案 0 :(得分:2)

至于为什么会发生这种情况,我猜想Matthew Watsons关于不等待最后一个退出线程的建议可能是正确的。

但我想补充一点,你可能不希望通过开始所有自己的线程来做到这一点。 Parallel.For将更有效地为您处理此问题,并解决所有事情完成时的问题。例如用这个替换上面的循环。

Parallel.For(0, counter, Do)

答案 1 :(得分:1)

该代码不应导致index重复的问题(因为您已经避免了关闭问题)。但是,Dictionary<>不是线程安全的,因此list.Add(index, index)不安全。

您可以在添加到列表之前通过锁定解决该问题,如下所示:

static void Do(int index)
{
    lock (list)
        list.Add(index, index);
}

你还提到list.Count并不总是正确的。这可能是因为您在最后一个线程完成添加之前检查了计数。

您可以在检查计数之前暂停一会儿来测试这个想法。 (但是,睡眠通常是错误的线程同步方法 - 但是对于这样的快速测试来说没问题。只是不要在生产代码中执行!)