识别InvalidOperationException"集合已被修改;枚举操作可能无法执行."

Ian*_*Ian 3 c# invalidoperationexception .net-2.0

我有一个好老的InvalidOperationException被抛出标准信息

收集被修改; 枚举操作可能无法执行.

问题是,枚举器不会自行修改,例如:

private TRoute _copyRoute(TRoute route)
{
    TRoute tempRoute = new TRoute();
    tempRoute.Initialize(route.Resource);

    foreach (TVisit visit in route)
    {
       tempRoute.Add(visit);
    }
    tempRoute.EndLocation = route.EndLocation;
    return tempRoute;
}
Run Code Online (Sandbox Code Playgroud)

我的代码是多线程的(本例中大约12-15个线程),每个线程应该在自己的路由深度克隆上工作.显然某些地方出了问题,但是,我的问题是如何通过如此多的线程来追踪这一点?减少数量可以显着阻止问题的显现.

在这种情况下,我的路由实例是一个IList,所以我可以玩添加到界面的东西.它下面有它自己的List实现.

编辑

只是添加,我可以ToArray()或ToList()这可能忽略这里的问题,但我真的不想那样做,我想找到原因.例如:

如果我将其更改为以下内容:

private TRoute _copyRoute(TRoute route)
{
    TRoute tempRoute = new TRoute();
    tempRoute.Initialize(route.Resource);

    foreach (TVisit visit in route.ToList())
    {
       tempRoute.Add(visit);
    }
    tempRoute.EndLocation = route.EndLocation;
    return tempRoute;
}
Run Code Online (Sandbox Code Playgroud)

然后我在这个断言上失败了,因为在ToList()之前发生了一次机会......我需要尝试找出发生变化的地方

TRoute tempRoute1 = CopyRoute(route1);
TRoute tempRoute2 = CopyRoute(route2);
Debug.Assert(tempRoute1.Count == route1.Count);
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 5

这是你可以用来包装你的东西IList<T>- 它检查它是在每次写操作的正确线程上.当然,在另一个线程上进行迭代而在另一个线程上进行迭代仍然是不安全的,但我认为这不是问题.(你总是可以调用CheckThread所有操作,而不仅仅是写的.)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;

class ThreadAffineList<T> : IList<T>
{
    private readonly Thread expectedThread;
    private readonly IList<T> list;

    public ThreadAffineList(IList<T> list)
    {
        this.list = list;
        this.expectedThread = Thread.CurrentThread;
    }

    private void CheckThread()
    {
        if (Thread.CurrentThread != expectedThread)
        {
            throw new InvalidOperationException("Incorrect thread");
        }
    }

    // Modification methods: delegate after checking thread
    public T this[int index]
    {
        get { return list[index]; }
        set
        {
            CheckThread();
            list[index] = value;
        }
    }

    public void Add(T item)
    {
        CheckThread();
        list.Add(item);
    }

    public void Clear()
    {
        CheckThread();
        list.Clear();
    }

    public void Insert(int index, T item)
    {
        CheckThread();
        list.Insert(index, item);
    }

    public bool Remove(T item)
    {
        CheckThread();
        return list.Remove(item);
    }

    public void RemoveAt(int index)
    {
        CheckThread();
        list.RemoveAt(index);
    }

    // Read-only members
    public int Count { get { return list.Count; } }
    public bool IsReadOnly { get { return list.IsReadOnly; } }

    public IEnumerator<T> GetEnumerator()
    {
        return list.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public bool Contains(T item)
    {
        return list.Contains(item);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        list.CopyTo(array, arrayIndex);
    }

    public int IndexOf(T item)
    {
        return list.IndexOf(item);
    }
}
Run Code Online (Sandbox Code Playgroud)