【问题标题】:InvalidOperationException: Collection was modified; refers to which collection?InvalidOperationException:集合已修改;指的是哪个集合?
【发布时间】:2013-01-22 23:42:31
【问题描述】:

我经常发现并没有明确说明是什么确切的集合导致了这种类型的异常。这是真的还是应该很明显?也许我只是不明白如何正确解释异常消息..

我特别想知道这个。它指的是什么收藏?

事件委托的参数很简单(对象发送者),引发的事件传递空参数。虽然引发事件的类本身继承了一个列表:

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;
    }

【问题讨论】:

  • 告诉我们Method的定义。异常的根本原因不在该行上。
  • 好的,添加了我认为相关的内容。
  • 这里仍然没有引用任何集合的代码。 instrument.DataSeries.GetTimeSerieByInterval 没有显示,ClearAndLoadAllHistory 也没有显示,这两种方法听起来都像是与集合交互的方法。
  • 我不相信你知道什么是相关的,什么是不相关的(不要说粗鲁,只是说;你不知道问题出在哪里)。查看顶级异常的 InnerException 属性,看看它是否会引导您。
  • @Ed S;我可以,但这不是我得到例外的地方。也许它可以被复制,但它不是一个常见的例外。但是请再次阅读我的问题,我并不是真的在询问错误,而是如何以及是否可以解释异常消息以识别集合。这是我最希望拥有的理解。

标签: c# .net multithreading invoke invalidoperationexception


【解决方案1】:

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

如果您可以在附加调试器时重现问题,然后使用调试 + 异常,请勾选 CLR 异常的抛出复选框。抛出异常时调试器停止,为您提供良好的语句位置和调用堆栈以供查看。

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

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

【讨论】:

  • 谢谢!也不知道 BeginInvoke。如果我可以跟进;如果两个工作线程“大约同时”调用 BeginInvoke 会发生什么。我们是否确保第一个启动将在启动下一个之前完全完成?
  • UI 线程一次只能运行一种方法。这些调用会自动序列化,先到先得。
【解决方案2】:

您似乎正在使用计时器。也许您正在从计时器回调(另一个线程)更改一些集合(更有可能是 UI 元素的属性)而不使用 Control.Invoke() 调用?

【讨论】:

  • 我没有使用计时器。但是 TimeSeries 集合从其他线程上的套接字接收更新,这会触发 HistoryUpdateEvent。并且用户可以用鼠标滚动,每次滚动都会从 TimeSeries 对象列表中更改 TimeSeries 对象。正是在此滚动期间发生了异常。
  • @bretddog,所以,套接字线程使用 Control.Invoke() 更改了 TimeSeries 集合?如果不是,那么这就是您得到异常的原因:当您滚动时,UI 线程检测到来自另一个(非 UI)线程的更改。必须使用Control.Invoke()Control.BeginInvoke() 进行与UI 相关的更改。
  • yes TimeSeries 集合被更改,并触发事件 HistoryUpdate,它触发 InvokeOnUpdate,它通过 Invoke 调用 Method。但同时我正在滚动,更改 UserControl TimeSerie 对象。可能我应该在更改 TimeSerie 对象时锁定一个锁定对象,并在 Method() 内部锁定该对象,由其他线程调用。我想这就是原因/解决方案。但我的主要问题是,如果我可以从异常消息中看到导致它的集合是什么。因为我也在注销和注册处理程序,所以可能是处理程序的集合。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-11
  • 1970-01-01
  • 2010-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-11
相关资源
最近更新 更多