给定样本类别:

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/

10-10 10:25