微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Parallel.ForEach中的C#List.Find有时返回NULL

如何解决Parallel.ForEach中的C#List.Find有时返回NULL

使用以下代码,通常一切正常。但是,极少数情况(也许每天一次,每天执行一次代码,每月一次).Find()方法找不到所需的项目。

List<string> names = new List<string>();
names.Add("bob");
names.Add("henry");
names.Add("mary");

List<Person> peopleList = GetlistofPeople();

Parallel.ForEach(names,(name) => {
    Person personFound = peopleList.Find(p => p.Name.Equals(name));
    int varThatCantBeNULL = personFound.Age;
});

有时,peopleList.Find()将返回一个对象,但其字段为NULL。我怎么知道的?

  1. 重新运行该应用程序可以正常工作,而不会再次出现问题(输入或状态不变,不会改变peopleList的内容
  2. 在发生问题时添加代码以打印peopleList的内容表明,列表中存在满足传递给Find()的条件的项目,并且其字段不是NULL

我无法重现问题,必须等待它自然/自然地发生。

我假设这与并行性和试图从同一列表中读取的多个线程有关。我已经通过捕获错误并重新尝试来解决问题,

谁能提供导致这种情况发生的原因的信息吗?我知道从多个线程写入一个公共变量是有问题的(竞争条件),但我不明白为什么从一个公共列表中读取会导致这种情况问题。

编辑

感谢您的答复。似乎所有迹象都表明我对personFound的使用不是线程安全的,因为其他线程正在向其写入(实际上,有代码正在写入我要访问的字段并遇到问题)。

但是,此线程Can I use local variables inside a Parallel Foreach loop (without unintentionally rewriting the previous value)中的答案表明我正在做的事情是线程安全的。似乎有点矛盾。它确实说ADO.NET类不是线程安全的,我使用的是自定义类而不是ADO.NET。

解决方法

由于您说过在阅读时从未写过该列表-因此阅读本身不会引起任何问题。当只有读者和没有作者的时候,所有结构都是线程安全的,这就是为什么不变结构如此流行的原因-您不能对其进行修改,因此它们在设计上是线程安全的。

但是,您是在修改存储在列表中的Person条目的字段,同时从其他线程中读取这些字段。这当然是不安全的。

看来您相信

Person personFound = peopleList.Find(p => p.Name.Equals(name));

以某种方式创建一个副本,并且保证对personFound字段的后续访问将返回与您找到它时相同的值。并非如此,如果Person是引用类型(我们可以放心地假设它是引用类型)。

.NET中有两种不同的类型:引用类型和值类型。引用类型种类的变量存储一个地址,通过遵循该地址,您可以到达存储在其他位置的实际内容。

在您的情况下-personFound变量确实是“本地”的,但是它仅包含实际内容的地址,并且所提及的内容由其他线程更改。因此,当您这样做时:

int varThatCantBeNULL = personFound.Age;

这很愚蠢,意味着-转到存储personFound内容的位置,然后从那里获取Age。此时:

Person personFound = peopleList.Find(p => p.Name.Equals(name));

Age可能不为null,但是在当前线程有机会在下一行读取Age之前-另一个线程可能已经对其进行了修改。

您在编辑中链接的问题-情况不一样,因为他们有

 string strType = drow["type"]

虽然字符串也是引用类型-它是不可变的,并且在创建后不能更改(“修改”字符串的方法只是创建一个副本,修改该副本并返回它)。因此,如果strType的内容不能被其他线程更改。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。