已使用锁并且仍然没有循环,异常 System.InvalidOperationException:集合已修改;枚举操作可能无法执行

lock used and there is no looping still, Exceptions System.InvalidOperationException: Collection was modified; enumeration operation may not execute

我已经经历过类似的问题,其中的答案建议使用 lock 而不是在同一集合的 foreach 循环中修改列表,我已经处理了这两个问题.

我有全局可用的 List<Object> 并且有两个不同的线程分别在列表中读取和写入。

为了避免竞争条件,我在两者(阅读[使用 linq] 和写作)中都使用了 Lock。 而且我正在创建本地副本,修改它并将该副本恢复到主副本(以减少锁定时间)

I guess while creating a local copy, it is having reference of main copy and thus main copy is being updated out side of lock too. Correct me here!

包含类似代码供参考,

    private void UpdateList(List<Object> newList)
    {
       List<Object> localCopyOfList;
       lock(m_lockObj) //again a global object for locking
       {
          //I suspect it is reference type's problem here
          localCopyOfList = m_globalCopyOfList; 
       }
       foreach(Object obj in newList)
       {
          localCopyOfList.Add(obj);
       }
       lock(m_lockObj)
       {
          m_globalCopyOfList = localCopyOfList; 
       }
   }

对于阅读,我有这样的方法,

private int GetTotalCount()
{
   List<Object> localCopyOfList;
   lock(m_lockObj) //again a global object for locking
   {
      localCopyOfList = m_globalCopyOfList; 
   }
   //On following line I'm getting this exception
   //Exceptions System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
   return localCopyOfList.where(x => x.status == true).Count;
}

I suspect it is reference type's problem here.

是的。您不是在创建新列表,您只是将引用复制到同一个列表。

要根据现有值创建新列表,您可以使用 List copy constructor:

List<Object> localCopyOfList;
lock(m_lockObj) //again a global object for locking
{
    //I suspect it is reference type's problem here
    localCopyOfList = new List<Object>(m_globalCopyOfList); 
}
foreach(Object obj in newList)
{
    lockCopyOfList.Add(obj);
}
lock(m_lockObj)
{
    m_globalCopyOfList = localCopyOfList; 
}

请记住,localCopyOfList 不再引用与 m_globalCopyOfList 相同的集合,但它的元素引用了。

您只是将引用复制到锁中的同一列表。因此它们是同一个实例。您需要为本地创建一个新列表,并将全局列表中的内容复制到您的锁内。

我认为不需要任何锁定。

private void UpdateList(List<Object> newList)
{
  //only read operation on global list, adding  items only to local list  
  newList.AddRange(m_globalCopyOfList);  
  //reference assignment is atomic
  m_globalCopyOfList = newList;
}

private int GetTotalCount()
{
   //reference assignment is atomic
   var localCopyOfList = m_globalCopyOfList; 
   //if UpdateList method is called anytime during below operation, 
   //m_globalCopyOfList ref will point to new list, localCopyOfList will not 
   //be touched.  
return localCopyOfList.where(x => x.status == true).Count;
}

@Amit 我制作了包含 250 个任务的演示程序,每个任务调用 updatelist 和 getlist。我没有收到任何异常或数据丢失。不确定我是否遗漏了任何东西,但请检查下面的代码

class Program
{
    static int Main(string[] args)
    {

        var obj = new Demo();
        var t = Enumerable.Range(1, 500)
        .Select((x, index) => Task.Run<int>(() => index %2 == 0 
                                            ? obj.UpdateList(new List<object>() { new { status = true } }) 
                                            : obj.GetTotalCount())).ToArray();
        Task.WaitAll(t);
        Console.Write(obj.GetTotalCount());
        Console.ReadLine();
        return 1;
    }

    public class Demo
    {
        private List<Object> m_globalCopyOfList { get; set; }
        public Demo() {
            m_globalCopyOfList = new List<object>();
        }
        public int UpdateList(List<Object> newList)
        {
            //only read operation on global list, adding  items only to local list  
            newList.AddRange(m_globalCopyOfList);
            //reference assignment is atomic
            m_globalCopyOfList = newList;
            return 1;
        }

        public int GetTotalCount()
        {
            //reference assignment is atomic
            var localCopyOfList = m_globalCopyOfList;
            //if UpdateList method is called anytime during below operation, 
            //m_globalCopyOfList ref will point to new list, localCopyOfList will not 
            //be touched.
            Console.WriteLine("Current count " + localCopyOfList.Count); 
            return localCopyOfList.Count;
        }
    }
}