InvalidOperationException:Collection已被修改;指的是哪个集合?

时间:2013-01-22 23:42:31

标签: c# .net multithreading invoke invalidoperationexception

我经常发现它没有真正指定究竟是什么样的集合导致了这种类型的异常。这是真的还是应该是显而易见的?也许我只是不明白如何正确解释异常消息..

具体来说,我想知道这个。它指的是什么系列?

事件委托的参数只是(对象发送者),并且引发的事件传递null参数。虽然引发事件的类本身继承了一个列表:

public class TimeSerie : List<BarData>

这里是否清楚“集合”是指引发事件的对象,还是可以是另一个对象?可以这么说,一个动态改变的方法的事件处理程序的集合?或者这会产生不同的例外吗?

    ************** Exception Text **************
System.InvalidOperationException: 
Collection was modified; enumeration operation may not execute.
   at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
   at System.Windows.Forms.Control.Invoke(Delegate method, Object[] args)
   at System.Windows.Forms.Control.Invoke(Delegate method)
   at SomeNameSpace.SomeUserControl.InvokeOnUpdateHistory(Object sender) in D:\SomePath\SomeUserControl.cs:line 5179
   at OtherNameSpace.OtherClass.TimeSerie.HistoryUpdateEventHandler.Invoke(Object sender)

UserControl中发生异常:

    public class SomeUserControl 

    private void InvokeOnUpdate(object sender)
    {
    this.Invoke(new GenericInvoker(Method));   // << Exception here!
    }

    private void Method() {...}

编辑: 添加了一些代码。有点简化,但认为它包含相关位。

private void Method() 
{
            if (this.instrument == null) return;  
            UnRegisterTimeSerieHandlers(this.ts);

            this.ts = instrument.DataSeries.GetTimeSerieByInterval(interval);
            if (ts != null)
            { 
                RegisterTimeseriesHandlers(ts);
                ClearAndLoadAllHistory();
            }
}

    private void UnRegisterTimeSerieHandlers(TimeSerie ts)
    {
        if (ts != null)
        {
            ts.TickUpdate -= InvokeUpdateCurrentBar;
            ts.NewBarUpdate -= InvokeUpdateNewBar;
            ts.HistoryUpdate -= InvokeOnUpdateHistory;
            this.ts = null;
        }
    }

    private void RegisterTimeseriesHandlers(TimeSerie ts)
    {
        ts.TickUpdate += InvokeUpdateCurrentBar;
        ts.NewBarUpdate += InvokeUpdateNewBar;
        ts.HistoryUpdate += InvokeOnUpdateHistory;
    }

2 个答案:

答案 0 :(得分:5)

是的,使用Control.Invoke()时,很难诊断异常的原因。问题是它在UI线程上发生异常并在工作线程中重新抛出异常时捕获异常。必然如此,您的工作线程需要知道Invoke()的返回值不可用。不可避免的副作用是你失去了圣堆痕迹,告诉你它在哪里爆炸以及它是如何到达那里的。

如果您在连接调试器时可以重新解决问题,请使用Debug + Exceptions,勾选CLR Exceptions的Thrown复选框。抛出异常时调试器会停止,为您提供一个好的语句位置和一个调用堆栈来查看。

如果没有,那么请考虑使用Control.BeginInvoke()。这是Invoke()的即发即弃版本,因此如果调用的方法抛出,那么将在UI线程上引发该异常,并且您将获得准确的堆栈跟踪。

通常,您总是希望使用BeginInvoke()。它不会导致工作线程停止,它避免了许多死锁情况并提供了良好的异常反馈。使用Invoke()通常是一个错误。

答案 1 :(得分:0)

好像你正在使用计时器。也许你是在不使用Control.Invoke()调用的情况下从定时器回调(另一个线程)更改某些集合(更有可能是集合是UI元素的属性)?