奇怪的InvokeRequired问题

Asi*_*sik 5 c# multithreading winforms

我有一个带有TreeView控件的UserControl,名为mTreeView.我可以从多个不同的线程获取数据更新,这些更新会导致TreeView更新.为此,我设计了以下模式:所有数据更新事件处理程序必须获取锁,然后检查InvokeRequired; 如果是这样,请通过调用Invoke来完成工作.这是相关的代码:

  public partial class TreeViewControl : UserControl
  {  
    object mLock = new object();
    void LockAndInvoke(Control c, Action a)
    {
      lock (mLock)
      {
        if (c.InvokeRequired)
        {
          c.Invoke(a);
        }
        else
        {
          a();
        }
      }
    }

    public void DataChanged(object sender, NewDataEventArgs e)
    {
      LockAndInvoke(mTreeView, () =>
        {
          // get the data
          mTreeView.BeginUpdate();
          // perform update
          mTreeView.EndUpdate();
        });
    }    
  }
Run Code Online (Sandbox Code Playgroud)

我的问题是,有时,在启动时,我会在mTreeView.BeginUpdate()上得到一个InvalidOperationException,说mTreeView是从一个不同于它创建的线程更新的.我在调用堆栈中返回到我的LockAndInvoke,并且看,c.InvokeRequired是真的但是其他分支已被占用!就像在调用else分支后,在另一个线程上将InvokeRequired设置为true一样.

我的方法有什么问题,我该怎么做才能防止这种情况发生?

编辑:我的同事告诉我,问题是InvokeRequired是错误的,直到创建控件,所以这就是它在启动时发生的原因.但他不知道该怎么办.有任何想法吗?

Han*_*ant 7

这是一个标准的穿线比赛.在创建TreeView之前,您将很快启动该线程.因此,您的代码将InvokeRequired视为false,并在创建本机控件后一瞬间失败.通过仅在窗体的Load事件触发时启动线程来修复此问题,第一个事件保证所有控件句柄都有效.

代码中的一些误解btw.使用是不必要的,InvokeRequired和Begin/Invoke都是线程安全的.而InvokeRequired是一种反模式.您几乎总是知道该方法将由工作线程调用.因此,只使用InvokeRequired在异常时抛出异常.哪个可以提前诊断出这个问题.