并行循环,添加到列表

时间:2014-03-28 13:00:09

标签: c# .net

是否有可能将循环长度增加的循环并行化?

List<int> list = new List<int>() { 0, 1 };

for (int i = 0; i < list.Count; i++)
//Parallel.For(0, list.Count, (i) =>
{
    Console.WriteLine(list[i]);
    if (i == 0) list.Add(2);
}//);

//foreach (int i in list)
//Parallel.ForEach(list, (i) =>
//{
//    Console.WriteLine(i);
//    if (i == 0) list.Add(2);
//}//);

Console.ReadLine();

在这个简单的例子中,预期的输出是:

0
1
2

上面的代码可以正常使用序列号&#39; for&#39;但是对于序列号#fore;&#39;由于集合被修改。对于两个并行化的实现,代码完成但输出缺少最终的&#39;

2 个答案:

答案 0 :(得分:3)

更改for循环中的集合无效。基本上以任何方式修改列表会使枚举器无效。以下是IEnumerator文档的引用:

  

只要集合保持不变,枚举器仍然有效。如果对集合进行了更改,例如添加,修改或删除元素,则枚举数将无法恢复,并且其行为未定义。

有关详细信息,请查看this post。就并行实现而言:

  • Parallel.ForEach - 这符合相同的IEnumerator问题,每个问题的标准都是
  • Parallel.For - 这将循环次数作为常量传递给for,而不是作为参考。这意味着当计数发生变化时,它不会改变它循环的次数

更安全的模式是在调用parallels实现之前添加,删除和修改列表元素。然后线程可以处理这些元素。如果无法做到这一点,那么确定循环后您将拥有的元素数,然后使用数组按索引存储/处理这些元素。最后将任何非空值拉回到列表中。这样您就不必担心列表的线程安全问题(Insert会推动其他元素前进,使您的索引无效)。以下应该有效:

// EX: might be initialized with a call to the database: "COUNT(id)"
int expectedElements = 10;
if (myList.Count < expectedElements)
  for (var idx = myList.Count; idx <= expectedElements; idx++) myList.Add(null);

var elements = myList.ToArray();
System.Threading.Tasks.Parallel.For(0, expectedElements, (idx) =>
{
  // "remove" the element
  if (idx % 3 == 0) elements[idx] = null;

  // "modify" the element
  if (idx % 3 == 1) elements[idx] = DifferentElement(idx);

  // "add" an element
  if (idx % 3 == 2) elements[idx] = GetNewElement(idx);
});

// clear current list, add new elements, remove null values
myList.Clear();
myList.AddRange(elements);
myList.RemoveAll(item => item == null);

现在您可以根据需要“添加”,“删除”和“修改”,结果会回到列表中!

答案 1 :(得分:0)

for (int i = 0; i < list.Count; i++) //list.Count will only checked at first call
{
    Console.WriteLine(list[i]);
    if (i == 0) list.Add(2);
}

听起来像你的list.Count会被问到一个,然后它将被保存在内存中,在你的情况下,list.Count将是2并且永远不会移动,所以你将打印list [0]然后列出[1]。
您可能也对锁定感兴趣:

主题A:

lock (list) {
    foreach (Object obj in list) {
        obj.doSomething();
        if(meet_condition) list2.add(obj)
    }
}

其中list2是静态属性。

主题B:

lock (list) {
  list.Remove(Element);
}

一旦线程锁定列表,其他线程一直等到它被释放才能使用它。 在不知道你试图用它做什么的情况下,很难帮助你。