给定样本类别:
internal Stuff<T> : IEnumerable<T> where T : Foo
{
private readonly object Sync = new object();
private readonly T[] TObjects;
public IEnumerator<T> GetEnumerator()
{
lock(Sync)
using (IEnumerator<T> safeEnum = TObjects.AsEnumerable().GetEnumerator())
while (safeEnum.MoveNext())
yield return safeEnum.Current;
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
//other things
}
如果我在foreach循环中迭代此类,则整个foreach循环将由对象的锁定方案锁定。
我的问题是,如果我在linq查询中使用它,会发生什么情况,例如:
Stuff stuff = new Stuff() { /*some foos*/ };
var things = stuff.Where(/*some predicate*/);
foreach(Foo foo in things)
{
//am i locked?
}
foreach(Foo foo in stuff.Where(/*some predicate*/))
{
//am i locked?
}
因此归结为,查询链接在后台如何工作?
最佳答案
让我们详细检查您的代码,除了stuff<T>
应该是一个类的小错字之外,您对IEnumerator<T>
的实现如下:
public IEnumerator<T> GetEnumerator()
{
lock (Sync)
using (IEnumerator<T> safeEnum = TObjects.AsEnumerable().GetEnumerator())
while (safeEnum.MoveNext())
yield return safeEnum.Current;
}
这是
Enumeration
的实现,而不仅仅是Enumerator
,因为获取枚举器后,您将通过进一步移动枚举器来进一步处理集合,理想情况下,您的自定义实现应如下所示:IEnumerator IEnumerable.GetEnumerator() => TObjects.AsEnumerable().GetEnumerator()
,因为枚举由foreach
循环完成,该循环通过访问实现的枚举器并调用MoveNext
方法和Current
属性来完成,因此当前的实现可能更加简洁枚举器实现
TObjects.AsEnumerable().GetEnumerator()
实现如下,它正在访问静态类GetEnumerator()
的System.Linq.Enumerable
实现,该静态类在内部具有抽象类Iterator<T>
的实现。AsEnumerable()
是一种简单的扩展方法 public static IEnumerable<TSource> AsEnumerable<TSource>(this IEnumerable<TSource>
source)
{
return source;
}
现在,由于我们的源是数组
T[]
,因此我们正在应用Where
子句,因此,如以下Enumerable实现中所指定 public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
if (source == null) throw Error.ArgumentNull("source");
if (predicate == null) throw Error.ArgumentNull("predicate");
if (source is Iterator<TSource>) return ((Iterator<TSource>)source).Where(predicate);
if (source is TSource[]) return new WhereArrayIterator<TSource>((TSource[])source, predicate);
if (source is List<TSource>) return new WhereListIterator<TSource>((List<TSource>)source, predicate);
return new WhereEnumerableIterator<TSource>(source, predicate);
}
我们得到
WhereArrayIterator
,其实现针对枚举/迭代如下: class WhereArrayIterator<TSource> : Iterator<TSource>
{
TSource[] source;
Func<TSource, bool> predicate;
int index;
public WhereArrayIterator(TSource[] source, Func<TSource, bool> predicate) {
this.source = source;
this.predicate = predicate;
}
public override Iterator<TSource> Clone() {
return new WhereArrayIterator<TSource>(source, predicate);
}
public override bool MoveNext() {
if (state == 1) {
while (index < source.Length) {
TSource item = source[index];
index++;
if (predicate(item)) {
current = item;
return true;
}
}
Dispose();
}
return false;
}
public override IEnumerable<TResult> Select<TResult>(Func<TSource, TResult> selector) {
return new WhereSelectArrayIterator<TSource, TResult>(source, predicate, selector);
}
public override IEnumerable<TSource> Where(Func<TSource, bool> predicate) {
return new WhereArrayIterator<TSource>(source, CombinePredicates(this.predicate, predicate));
}
}
如注释中所述,将枚举数获取到集合是线程安全的,因为它是只读的。提及的所有与代码和类相关的详细信息都可以在MS Reference Source上找到,该地址具有API搜索浏览器
关于c# - 锁定在GetEnumerator()内部...在带有LINQ扩展的foreach中会发生什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52564852/