我很少怀疑在多线程应用程序中如何访问共享IEnumerable
和IQueryable
。
请考虑此代码段。
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()
是异步的,这意味着所有文件的所有foreach
中的所有SomeHandler
将并行执行(每个文件都在自己的线程中), IEnumerable
的共享实例按预期/正常枚举?这是一种正确的方法吗?我可以在多个线程中共享相同的IEnumerable
实例,并且并行枚举它吗?
如果我在上面的代码中使用IQueryable
而不是IEnumerable
怎么办?我应该注意哪些副作用?
如果它不是线程安全的,那么我应该使用什么?
请注意我使用IQueryable
进行数据库查询,因为我不想从数据库中提取所有数据。因此,我想尽可能避免IQueryable.ToList()
。
答案 0 :(得分:5)
这取决于实施。 IEnumerable<T>
的某些实现也会实现IEnumerator<T>
,并从GetEnumerator()
返回。在这种情况下,它显然不是线程安全的......
至于IQueryable<T>
,它还取决于实施。例如,Entity Framework上下文不是线程安全的,只能在创建它们的线程上正常工作。
所以这个问题没有独特的答案......它可能适用于某些实现,而不适用于其他实现。
答案 1 :(得分:1)
当你作为参数传递给委托时,我会ToList()
你的枚举实际创建一个新的集合供线程使用并避免问题。
但是,我想知道为什么你需要枚举枚举枚举的每个元素N次(有效N ^ 2)?这听起来效率低下。
编辑:更新了我的意图
答案 2 :(得分:0)
答案 3 :(得分:0)
嗯,如果使用非线程安全的IQueryable(如实体框架查询),可以做的一件事就是在一个线程中枚举结果,然后传递结果必要的新线程。
然后,如果IQueryable / IEnumerable是线程安全的并不重要。