【问题标题】:C# InvalidOperationException AND Cross-thread operation [duplicate]C# InvalidOperationException 和跨线程操作 [重复]
【发布时间】:2010-12-08 08:32:54
【问题描述】:

在我的 Windows 窗体中,我有一个文本框和一个按钮,文本框“tb_LogBox”是多行文本框我编译并运行它 Visual Studio 抛出 InvalidOperationException。

我得到的实际错误 跨线程操作无效:控件 'tb_LogBox' 从创建它的线程以外的线程访问。以下示例代码说明了我正在尝试做的事情

private void button1_Click(object sender, EventArgs e)
{
    try
    {
        var bw = new BackgroundWorker();
        bw.DoWork += ExecuteOperations ;
        bw.RunWorkerAsync();
    }
    catch (Exception ex)
    {
        tb_LogBox.AppendText(Environment.NewLine + " =@= " + ex.Message+" "+ex.Source);
    }
}

private void ExecuteOperations(object sender, DoWorkEventArgs e)
{
    var FuncCall = new LogTimer();
    tb_LogBox.AppendText(Environment.NewLine + FuncCall.DnT()); // the line i am getting the error. on 
}

public class LogTimer
{
    public string DnT()
    {
        const string datePat = @"d/MM/yyyy";
        var dateTime = DateTime.Now();
        return dateTime.ToString(datePat);
    }
}

【问题讨论】:

    标签: c# .net visual-studio


    【解决方案1】:

    尝试使用开始调用方法:

        BeginInvoke(new Action(() =>
        {
           tb_LogBox.AppendText(Environment.NewLine + FuncCall.DnT());
        }));
    

    这会比 Invoke 更流畅。

    【讨论】:

      【解决方案2】:

      您需要将 Ui 更改编组到 UI 线程。这可以通过在 tb_LogBox.AppendText 周围使用 invoke/begininvoke 调用来执行

      在 Winforms 应用程序中:

          this.BeginInvoke((MethodInvoker)delegate
              {
                  tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
              });
      

      在 WPF 应用程序中:

          this.Dispatcher.BeginInvoke(
              (Action)delegate()
              {
                  tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
              });
      

      希望这会有所帮助!

      【讨论】:

        【解决方案3】:

        在您的 ExecuteOperations 中执行此操作:

        tb_LogBox.Invoke((MethodInvoker)delegate() { tb_LogBox.AppendText(...) }));
        

        您不能使用其他线程(BackgroundWorker 使用 .NET 线程池线程)来更改 UI 组件。这是您在 WinForms 编程中必须习惯的主要障碍。

        【讨论】:

          【解决方案4】:

          BackgroundWorker 在自己的线程上执行,所有与 WinForms GUI 元素相关的操作都必须在创建它们的线程上运行。您当前使用 BackgroundWorker 的方式与使用 ThreadPool.QueueUserWorkItem() 对操作进行排队相同。要使用 BackgroundWorker 与 GUI 进行通信,请使用 ReportProgess 或在 worker 方法中设置 DoWorkEventArgs.Result 属性,并对 GUI 线程上的相应事件做出反应。您还可以在 WinForms 控件上使用 Invoke/BeginInvoke 直接在 GUI 线程上执行任意代码。在您的情况下,这意味着将访问 tb_LogBox 的行替换为:

          tb_LogBox.Invoke(new Action(() =>
              tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
          ));
          

          【讨论】:

            【解决方案5】:

            您需要在 UI 线程上调用控件的方法:

            private void ExecuteOperations(object sender, DoWorkEventArgs e)
            {
                var FuncCall = new LogTimer();
                tb_LogBox.Invoke((MethodInvoker)delegate{ 
                    tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
                });
            }
            

            我不知道 LogTimer 做了什么,但很可能您也应该在委托中创建它:

            private void ExecuteOperations(object sender, DoWorkEventArgs e)
            {
                tb_LogBox.Invoke((MethodInvoker)delegate{ 
                    var FuncCall = new LogTimer();
                    tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
                });
            }
            

            【讨论】:

              【解决方案6】:

              您无法从后台工作线程的执行线程访问主机线程。您可以使用 BackgroundWorker 的 ReportProgress 方法向宿主线程发送信息。

              private void button1_Click(object sender, EventArgs e)
              {
                  try
                  {
                      var bw = new BackgroundWorker();
                      bw.DoWork += ExecuteOperations;
                      bw.ProgressChanged += bw_ProgressChanged;
                      bw.RunWorkerAsync();
                  }
                  catch (Exception ex)
                  {
                      tb_LogBox.AppendText(Environment.NewLine + " =@= " + ex.Message + " " + ex.Source);
                  }
              }
              
              private static void ExecuteOperations(object sender, DoWorkEventArgs e)
              {
                  var FuncCall = new LogTimer();
                  string text = Environment.NewLine + FuncCall.DnT();
              
                  (sender as BackgroundWorker).ReportProgress(0, text);
              }
              private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
              {
                  tb_LogBox.AppendText(e.UserState as string);
              }
              
              public class LogTimer
              {
                  public string DnT()
                  {
                      const string datePat = @"d/MM/yyyy";
                      var dateTime = DateTime.Now;
                      return dateTime.ToString(datePat);
                  }
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-03-27
                • 2012-08-22
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多