【问题标题】:Why I can't use List.Add() on Parallel.For or Parallel.Foreach?为什么我不能在 Parallel.For 或 Parallel.Foreach 上使用 List.Add()?
【发布时间】:2014-07-08 20:52:02
【问题描述】:

这是我的代码:

Parallel.ForEach(Students, item =>
{
   StudentModel studentModel = new StudentModel(item);

   // Maybe he/she has alot of name
   foreach (var words in studentModel.StudentNames.Split(','))
   {
        if (string.IsNullOrWhiteSpace(words))
                return;

        string tempWords = words.Trim();
        // add it to student names list
        STUDENT_NAMES.Where(x => x.SearchWords == tempWords).FirstOrDefault().student.Add(studentModel);


     }
     // add it to student list
     STUDENT_MODELS.Add(studentModel);
});

我想做的是,我得到学生名单。将其转换为学生模型,获取学生姓名(因为一个学生有很多名字),然后我将姓名添加到姓名列表中,那是因为也许以后我需要获得同名学生并做一些事情..... 最后将学生添加到学生模型列表中。

问题发生在:

STUDENT_NAMES.Where(x => x.SearchWords == tempWords).FirstOrDefault().student.Add(studentModel);

这个地方总是发生:System.IndexOutOfRangeException

我已经将 Paralle.Foreach 更改为 Parallel.For ,并将 foreach 更改为 for,但没有任何改变。 我必须使用 Parallel,因为学生数大约 100000,如果我只使用 foreach 替换 Parallel.Foreach,则需要 160+ 秒,如果我锁定那个地方......仍然很慢......如果使用 Parallel。 Foreach,它将使用 20 秒左右,但我无法处理异常。

我已经尝试使用这个替换它:

StudentNames name = STUDENT_NAMES.Where(x => x.SearchWords == tempWords).FirstOrDefault();
if (null != name)
   name.student.Add(StudentModel);

但问题仍然在某些时候发生............ 如果我只是尝试...抓住它并忽略它,那么当我稍后访问 STUDENT_NAMES 列表时,它仍然会抛出该异常............

我也尝试使用 ConcurrentBag ,但是速度很慢......我受不了......

请问有什么好的办法吗。非常感谢!

更新:

我不明白的是:为什么我不能在 Parallel.Foreach 的列表中添加一些东西。我认为 Parallel.Foreach 会使用很多线程,但事件使用多线程添加没有任何问题。

thread 1 add 和 thread 2 add 没有任何关系....为什么会发生那个异常?

【问题讨论】:

  • I also try use ConcurrentBag<> , but the speed very slow....I can't take it 那就写个比ConcurrentBag更好的东西,可以同步访问。
  • 不确定您要做什么,但必须有比 STUDENT_NAMES.Where(x => x.SearchWords == tempWords).FirstOrDefault() 更有效的方法。使用哈希集。

标签: c# .net linq plinq


【解决方案1】:

List 不是为从多个线程访问而设计的。这样做是不安全的。你可能会遇到各种各样的问题,从索引外异常、添加丢失、重复、其他类型的错误,几乎任何事情。

我也尝试使用 ConcurrentBag ,但是速度很慢......我受不了......

当两个选项中只有一个真正有效时,性能无关紧要。您可以拥有正确运行的代码,并且需要尽可能长的时间,或者您可以拥有无​​法运行的代码,这与它执行错误的速度无关。

当然,您还有其他选择,例如寻找重新设计程序以使用本质上更有效的算法的方法,而不是尝试做一些非常低效的事情并通过并行化来隐藏这一事实。您当前正在列表中执行线性搜索以找到匹配的项目为另一个集合中的每个项目这一事实是一个相当糟糕的设计;您应该使用可以更有效地搜索的集合,例如HashSet。在不知道问题的细节的情况下,我们不可能说出应用程序的正确设计应该是什么。

【讨论】:

  • 每一个,如果你在 Parallel.Foreach 上有 XX.Add(),你只需要将 XX 类型从列表中更改为 hashset。问题将得到解决。谢谢@Servy。
  • @qakmak 不,它肯定是没有解决的。 HashSet 根本不是为同时从多个线程操作而设计的,这样做会导致各种令人讨厌的行为,就像 List 一样。几次测试没有发现问题,不代表没有,即使没有,也不代表以后不会有。
  • 我认为在发生索引问题之前因为列表使用索引排序,然后当某个线程添加一些对象时另一个线程也添加一些对象,但 HasSet 不使用索引排序,所以我认为问题解决了。因为我只需要将对象添加到 HasSet 使用 Parallel.Foreach,仅此而已。
  • @qakmak 这完全不正确。 HashSet 在没有适当同步的情况下在多线程环境中使用并不是完全安全的。你的解释完全不正确。两种数据结构都没有进行任何排序,两种数据结构都通过索引访问后备数组中的项目,它们只是在稍微不同的庄园中这样做。再一次,代码不安全。您还没有注意到错误并不意味着没有,也不意味着不会有任何错误。
  • 你说得对,有时还是索引出了问题。那我需要怎么做??
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-14
  • 2015-07-27
  • 1970-01-01
  • 2011-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多