6

当我在处理我们的框架的并行性时,我遇到了一个奇怪的情况,我无法想象这是为什么!我简化了情况以简单地描述它。考虑这段代码:

foreach(var person in personList)
{
    if (person.Name == "Mehran")
        break;
}

personList在多个线程之间共享。

在什么情况下有可能personnull并且我得到了NullReferenceExceptionfor person.Name

据我所知,这个人在这里被认为是一个局部变量,如果我们进入foreach块,那么我们已经personList成功地迭代了,所以person在任何情况下或任何并行情况下都不应该为空。

即使personList被另一个线程更改,或者被引用person的被释放,person变量也应该有一个值。因为没有人可以更改person引用的位置。

有什么场景可以解释这种情况吗?

4

2 回答 2

11

据我所知,这个人在这里被认为是一个局部变量,如果我们进入 foreach 块,那么我们已经成功地迭代了 personList,所以 person 在任何情况下或任何并行情况下都不应该为空。

仅仅因为您personList成功迭代并不意味着它不包含任何空值。例如:

List<Person> personList = new List<Person>();
personList.Add(null);

foreach (var person in personList)
{
   // Here, person will be null
}

(此外,如果有任何东西正在修改列表,那么您通常会遇到麻烦 - 在编写者面前它们不是线程安全的 - 但我认为这不需要成为问题的一部分。)

于 2012-12-05T17:46:23.883 回答
3

变量未修改。用于实现foreach构造的迭代器不是线程安全的:

此处List<T>.IEnumerable<T>.GetEnumerator()找到的文档中:

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

枚举器没有对集合的独占访问权;因此,通过集合进行枚举本质上不是线程安全的过程。为了保证枚举过程中的线程安全,可以在整个枚举过程中锁定集合。要允许集合被多个线程访问以进行读写,您必须实现自己的同步。

命名空间中集合的默认实现System.Collections.Generic不同步。

如果存在另一个线程可能修改列表的可能性,您应该在迭代列表时始终锁定列表。

于 2012-12-05T17:48:46.190 回答