C#Wpf Foreach InvalidOperationException多线程

时间:2013-11-12 22:30:00

标签: c# wpf multithreading canvas

我有一个Canvas里面有很多小画布。如果我单击它们中的任何一个,它会创建一个带有特定方法的Thread(例如,在主画布上添加另一个小画布并移动它)。当我点击一个特定按钮时,我需要让一些小画布成长。所以我尝试使用foreach循环但应用程序崩溃并向我显示此错误:

  

InvalidOperationException:修改了集合;枚举操作可能无法执行

我认为这是因为有一个上下文切换,所以我添加了一个锁定方法,但问题仍在继续。我的代码如下:

private void BacteriaPsiEspecial(object sender , MouseButtonEventArgs e) 
{
  lock (sender)//Maybe here is the problem
  {
    //The application crashes here
    foreach (Canvas LittleCanvas in CanvasSimulador.Children)
    {
      if(LittleCanvas.Uid.Equals("SomeId")) 
      {
        //Method to make each one grow growMethod()
      }
    }
  }
}

我不知道foreach是否有效。我必须使用很多线程,因为所有的小画布都在主画布上不断移动。问题是,我该怎样做才能使那些画布受到growMethod()的影响?

1 个答案:

答案 0 :(得分:0)

听起来你有多个线程会改变你试图foreach的集合。简单地说,这不起作用。以下是我能想到的几个选项:

  • 放置您正在循环的集合中修改(添加/删除)的所有内容。但是,这将部分消除使用多个线程的好处,并且根据您编写代码的方式,可能会导致死锁,因为没有线程可以继续进行,因为它们都在等待锁定。
  • 如果growMethod()需要花费大量时间,您可以在迭代之前尝试获取列表的副本...... LINQ扩展方法.ToList().ToArray()应该可以正常工作。但是你可能仍然会遇到异常,所以你最终可能会添加重试逻辑......等等。
  • 您可以通过在访问和修改时锁定来限制对集合的访问:

    private IEnumerable<Canvas> GetLittleChildren() {
      lock(lockObject) {
        return CanvasSimulador.Children.ToArray();
      }
    }
    
    private IEnumerable<Canvas> AddChild(Canvas littleCanvas) {
      lock(lockObject) {
        CanvasSimulador.Children.Add(littleCanvas);
      }
    }
    
    private IEnumerable<Canvas> RemoveChild(Canvas littleCanvas) {
      lock(lockObject) {
        CanvasSimulador.Children.Remove(littleCanvas);
      }
    }
    

    我仍然不相信这是一个好方法,但它可能会有用......