不一致的“跨线程操作无效”异常

Posted

技术标签:

【中文标题】不一致的“跨线程操作无效”异常【英文标题】:Inconsistent "Cross-thread operation not valid" exception 【发布时间】:2016-04-19 14:31:53 【问题描述】:

跨线程操作无效:控制''从线程访问 除了创建它的线程之外。

我的同事在我的代码中遇到了这个异常,但我没有。

在表单上,​​我有一个控件,用户可以在其中向 ListView 添加一些字符串。

我在控件中公开了一个属性,该属性返回 XmlDocument 中的字符串。

public XmlDocument XmlConfig

    get
    
        return GetXML();
    

Get XML 只是获取 ListViewItem 集合并将它们格式化为 xml 文档。

private XmlDocument GetXML()

    foreach(ListViewItem lvi in myListView.Items)   <-- Exception Here
    
         // Do Stuff
    

    为什么我在尝试阅读列表视图时会收到此信息?我认为跨线程异常是当您尝试从单独的线程更新控件时。

    为什么我也没有得到这个异常?

【问题讨论】:

【参考方案1】:

我认为跨线程异常是当您尝试从单独的线程更新控件时

不正确。当控件不是从主 UI 线程访问时会发生跨线程异常。解决方法很简单,检查InvokeRequired并使用Invoke

至于为什么您的朋友点击了这个而您没有点击,从您发布的代码中无法判断。可能是他针对不同的 .Net 目标版本进行编译,或者可能是您的硬件未命中的竞争条件(例如,他有更多内核)。重点是这无关紧要。您的代码需要是线程安全的,因为显然 GetXML 可以并且 is 从非主 UI 线程调用。检查InvokeRequired 的责任很可能在于调用者,而不是方法。同样,从发布的代码中无法分辨。

【讨论】:

【参考方案2】:

当在 UI 线程 之外访问Control 时(InvokeRequired 返回true),您应该使用Invoke insetead 直接调用;为了不重复代码(对于 UI 线程和其他线程),让我们使用扩展方法:

  private XmlDocument GetXML() 
    myListView.InvokeSynchronized(() => 
      foreach(ListViewItem lvi in myListView.Items) 
        // Do Stuff
      
    );
  

  ...

  public static class ControlAsyncExtensions 
    public static void InvokeSynchronized(this Control control, Action action) 
      if (Object.ReferenceEquals(null, action))
        throw new ArgumentNullException("action");

      if (Object.ReferenceEquals(null, control))
        action();
      else if (control.InvokeRequired)
        control.Invoke(action);
      else
        action();
    
  

【讨论】:

【参考方案3】:

我认为跨线程异常是在您尝试从单独的线程更新控件时发生的。

这是不正确的。请记住,大多数 WinForms 控件都是围绕 Windows 通用控件的包装器。 Windows 控件使用 messages 进行 检索 和更新,通常通过 SendMessage function。所以一般来说,虽然 一些 调用可以安全地从另一个线程执行,但出于安全原因,Control.CheckForIllegalCrossThreadCalls 用于每个 get/set 属性或方法调用的控件实现中。

为什么我也没有得到这个异常?

没关系,你一开始就不应该这样做。

【讨论】:

以上是关于不一致的“跨线程操作无效”异常的主要内容,如果未能解决你的问题,请参考以下文章

跨线程操作无效[重复]

获取跨线程操作无效[重复]

为啥我没有收到“跨线程操作无效”错误

例外:跨线程操作无效:控制'pgImportProcess(进度条)'从一个线程访问,而不是它在[重复]上创建的线程

跨线程操作无效:控件“statusStrip”从创建它的线程以外的线程访问

ContentDisposition 类抛出不一致的异常