【问题标题】:Event, GUI and Threads事件、GUI 和线程
【发布时间】:2011-11-04 00:37:23
【问题描述】:

我有父表单和子表单。父表单包含一个 dataGridView(帐户列表),子表单允许用户注册一个帐户。

子窗体在单独的线程上启动。

当帐户注册时,它被添加到 SQL 数据库中,并且在父表单订阅的子表单上触发事件。然后父窗体更新 dataGridView 以添加来自数据库的新值。

问题是在触发事件时尝试更新父表单中的 dataGridView 时,我得到一个跨线程错误。在这种情况下这是正常行为吗?

【问题讨论】:

  • 与 ui 控件的任何交互都必须在创建控件的线程中进行。假设您使用的是 win 表单,您需要使用 Control.BeginInvoke/Invoke 方法与在另一个线程中创建的控件

标签: c#


【解决方案1】:

在您的处理程序中,您必须做一些工作才能将处理程序带回 UI 线程...“规范”是使用 InvokeRequiredBeginInvoke 以下模式:

private void OnChildFormSaysNewItemsHandler(object sender, EventArgs e)
{
    // Bring on the UI thread
    if (this.InvokeRequired)
    {
        Action<object, EventArgs> handler = OnChildFormSaysNewItemsHandler;
        this.BeginInvoke(handler, sender, e);
        return;
    }

    // Do the normal work...
}

【讨论】:

  • 在事件处理程序中这样的事情就足够了吗? this.Invoke((MethodInvoker)delegate { fillData(); });
  • 在“做正常工作”部分后,您将返回 UI 线程,因此您将能够调用 FillData() 而无需担心调用(通常会同步调用某些内容)在 UI 线程上)。
【解决方案2】:

是的,这很正常,触发事件的线程是子线程表单。本次事件在主窗体中运行的代码在子窗体线程中运行。所以这很正常。 我鼓励你永远不要在不同的线程中启动表单、控件。留在主线程。但是您可以使用 BackgroundWorker 或其他任何东西在另一个线程中执行内部进程。

【讨论】:

    【解决方案3】:

    是的,您不能从创建它们的其他线程访问 UI 元素。您需要将调用编组到父表单所在的线程(如果您使用的是 winforms,则 InvokeRequired 是您的朋友)

    【讨论】:

      【解决方案4】:

      这种行为是意料之中的 - WinForms 应用程序模型在设计上不是线程安全的。 要与来自非 UI 线程的控件进行交互,请使用 Control.Invoke 或 Control.BeginInvoke() 方法。

      例子:

      void RefreshData()
      {
          // Refresh database here
      }
      
      void MyOtherThreadCallback()
      {
         this.BeginInvoke(new Action(RefreshData()))
      }
      

      【讨论】:

        【解决方案5】:

        您的父表单尝试使用触发事件的线程更新网格视图。这会导致跨线程错误。为避免这种情况,您必须使用创建控件的线程来更新控件。这通常使用代码来完成:

        if(control.InvokeRequired)
        {   
           control.Invoke(delegateToThisMethod)
        }
        

        并在delegateToThisMethod 中更新网格视图。

        这个问题在this question得到了很好的回答

        【讨论】:

          【解决方案6】:

          您没有列出您使用的版本,但请查看 MSDN 上的 BackgroundWorker Class,它使基本线程变得非常容易。

          你会想看看这些事件:

          OnDoWork               Raises the DoWork event.
          OnProgressChanged      Raises the ProgressChanged event.
          OnRunWorkerCompleted   Raises the RunWorkerCompleted event.
          

          DoWork 方法中发生的事情发生在一个单独的线程(以及它调用的任何东西)上,但是您创建的 ProgressChanged 和 RunWorkerCompleted 方法都在 UI 线程上运行,因此可以更新 UI 元素。

          【讨论】:

            猜你喜欢
            • 2013-05-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多