C#InvalidOperationException和跨线程操作

在我的Windows窗体中,我有一个文本框和一个按钮,文本框“tb_LogBox”是多行文本框我正在尝试创建一个后台工作程序,它应该调用一个函数,即编译时的“LogTimer.DnT()”是并运行它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); } } 

尝试使用begin invoke方法:

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

这比Invoke更顺畅。

你需要将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()); }); 

希望这可以帮助!

在ExecuteOperations中执行此操作:

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

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

您需要在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()); }); } 

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()); )); 

您无法从后台工作程序的执行线程访问主机线程。 您可以使用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); } }