【问题标题】:C# Getting to original thread to set textbox valuesC# 获取原始线程以设置文本框值
【发布时间】:2012-04-07 07:58:14
【问题描述】:

我的主窗体 Form1 正在运行我的程序的主要部分。

我有一个单独的线程开始执行算法。

当我从新线程运行方法时,方法 MyAlgorithm() 出现错误

InvalidOperationException 并显示消息“控制控件名称是从创建它的线程以外的线程访问的。”

如何返回原始线程,以便我可以运行该方法以使用最新值更新我的文本框?

这是我要运行的方法,包含在我的应用程序的主类 Form1 中。

// Reset the results values
public void ShowResults()
{
    while (true)
    {

        loopsNum.Text = Convert.ToString(resultLoopsNum);
        nodesVisitedNum.Text = Convert.ToString(resultNodesVisitedNum);
        nodesResolvedNum.Text = Convert.ToString(resultNodesResolvedNum);
        cpuLoopsNum.Text = Convert.ToString(resultCpuLoopsNum);
        shortestPathCostNum.Text = Convert.ToString(resultShortestPathCost);

    } 
}

我查看过 Invoke() 方法,但不知道如何从线程方法中获取 Form1 的原始实例。

我正在像这样调用我的线程...

        // Set the algorithm method up in it's own thread
        Thread thread = new Thread(new ThreadStart(MyAlgorithm));

        // Run the algorithm
        thread.Start();

【问题讨论】:

标签: c# multithreading thread-safety


【解决方案1】:

你可以使用:

myform.Invoke(ShowResults);

【讨论】:

    【解决方案2】:

    如何返回原始线程,以便我可以运行该方法以使用最新值更新我的文本框?

    在 Windows 窗体中,您可以或者使用Control.Invoke/BeginInvoke或者使用BackgroundWorker并在进度事件处理程序中执行更新。

    在 WPF 中,您将使用 Dispatcher.Invoke/BeginInvoke

    在 C# 5 和 .NET 4.5 中,您将能够使用异步方法,这将使这一切变得更加简单......

    我查看过 Invoke() 方法,但不知道如何从线程方法中获取 Form1 的原始实例。

    如果“线程方法”只是Form 的一个实例方法,那么您已经获得了this 引用。如果不是,您需要提供该信息 - 如果您可以单独表达“更新”部分,最好以ISynchronizeInvoke 的形式提供,以避免直接依赖 Windows 窗体。 (这个接口现在有点弃用了,有利于同步上下文,但它仍然工作得很好。)

    【讨论】:

      【解决方案3】:

      看看Control.Invoke():

      public void ShowResults()
      {
        while (true)
        {
          Thread.Sleep(1000); // don't spam the UI thread
      
          if (this.InvokeRequired) 
          {
            this.Invoke((Action)UpdateGui);
          }  
          else
          {
            UpdateGui();
          }
        } 
      }
      
      private void UpdateGui() 
      {  
        loopsNum.Text = Convert.ToString(resultLoopsNum);
        nodesVisitedNum.Text = Convert.ToString(resultNodesVisitedNum);
        nodesResolvedNum.Text = Convert.ToString(resultNodesResolvedNum);
        cpuLoopsNum.Text = Convert.ToString(resultCpuLoopsNum);
        shortestPathCostNum.Text = Convert.ToString(resultShortestPathCost);
      }
      

      【讨论】:

      • 感谢您的帮助,现在可以使用了。但我不太明白为什么它需要是一个Action 对象。 Invoke 何时不需要 Action 对象而不仅仅是函数名?
      • 那是因为该方法可用于调用任何方法,而不仅仅是具有给定签名的方法。看看this overload
      • 另外,你会发现一个很好的解释in this question
      【解决方案4】:

      这里还有其他选择:

      交替使用System.Forms.Timer 定期调用ShowResults。或者另一种选择是 not 使用另一个线程来执行操作;在 GUI 线程中执行此操作,并在您想让 GUI 更新时从操作中调用 Application.DoEvents()

      第一个选项很好,因为它可以防止您意外地用Invoke 请求淹没 GUI,第二个选项很好,因为它都在 GUI 线程上,并且允许您对显示内容的时间进行细粒度控制在 GUI 上。

      【讨论】:

        猜你喜欢
        • 2011-09-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-22
        • 2012-03-05
        • 2012-03-18
        相关资源
        最近更新 更多