在多线程应用程序中共享IEnumerable<T>和IQueryable<T>

5

关于多线程应用程序中如何访问共享的 IEnumerableIQueryable,我有一些疑问。

考虑以下代码片段。

ObservableCollection<SessionFile> files = /* some code */
IEnumerable<Pattern> allFilePatterns= /*some query */

foreach (Pattern pattern in allFilePatterns)
{
   string iclFilePath = Path.Combine(pattern.Location, pattern.Filename);
   SessionFile sfile = new SessionFile(iclFilePath, pattern.AnalysisDate);

   SomeDelegate invoker = new SomeDelegate(sfile.SomeHandler);
   invoker.BeginInvoke(allFilePatterns, null, null);

   files.Add(sfile );
}

如您所见,我正在使用BeginInvoke()相同的allFilePatterns实例传递给每个调用sfile.SomeHandler的处理程序。

假设在SomeHandler中,我使用foreach循环遍历allFilePatterns,像这样:

void SomeHandler(IEnumerable<Pattern> allFilePatterns)
{
    foreach(Pattern pattern in allFilePatterns)
    {
          //some code
    }
}

现在我的疑惑是:由于BeginInvoke()是异步的,这意味着所有文件中SomeHandler中的所有foreach都会并行执行(每个在自己的线程中),此时共享的IEnumerable实例是否会按预期/正常的方式进行枚举?这种方法正确吗?我可以在多个线程中共享同一个IEnumerable实例,并并行枚举它吗?
如果我在上面的代码中使用IQueryable而不是IEnumerable会发生什么?我应该注意哪些副作用?
如果它不是线程安全的,那么我应该使用什么?
请注意,我正在使用IQueryable进行数据库查询,因为我不想从数据库中拉取所有数据。因此,我希望尽可能避免使用IQueryable.ToList()
4个回答

5
这取决于具体实现方式。某些 IEnumerable<T> 实现也同时实现了IEnumerator<T>,并从 GetEnumerator() 返回自身。在这种情况下,显然不是线程安全的...
至于 IQueryable<T>,也要看具体实现方式。例如,Entity Framework 上下文不是线程安全的,只能在创建它们的线程上正常工作。
因此,对于这个问题没有唯一的答案... 它可能适用于某些实现,而对于其他实现则不适用。

1
我建议在将可枚举对象作为委托的参数传递时,使用ToList()来创建一个新的集合以供线程使用,以避免出现问题。
不过,我很好奇你为什么需要对可枚举对象的每个元素进行N次枚举(实际上是N^2)?这听起来很低效。
编辑:已根据我的意图进行更新。

这仍然取决于SessionFile和Pattern的线程安全性。 - sehe


0

嗯,如果您正在使用不是线程安全的IQueryable(例如Entity Framework查询),则可以做一件事情,那就是在一个线程中枚举结果,然后根据需要将结果传递给新线程。

这样,IQueryable / IEnumerable是否线程安全就无关紧要了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接