如何解决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。我怎么知道的?
- 重新运行该应用程序可以正常工作,而不会再次出现问题(输入或状态不变,不会改变peopleList的内容)
- 在发生问题时添加代码以打印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 举报,一经查实,本站将立刻删除。