对控件进行线程安全调用
本文关键字:安全 调用 线程 控件 | 更新日期: 2023-09-27 18:06:04
这很奇怪。
我有一个后台工作人员在我的Windows窗体上做一些工作。作为这项工作的一部分,更新了数据网格控件。
一旦这个过程完成,一切都很好。
如果我再次单击按钮启动后台工作程序并再次启动该过程,我会在以下代码中得到一个错误cross thread not valid
:
private void bgProcessing_Production_DoWork(object sender, DoWorkEventArgs e)
{
String[] args = (String[])e.Argument;
e.Result = args[0];
gvTaskCases.DataSource = null;
if (gvTaskCases.Rows.Count != 0) // EXCEPTION IS THROWN HERE!
{
gvTaskCases.Rows.Clear(); // .Update();
}
现在是这样的,正如我所说,它第一次运行良好。
但更糟糕的是,如果我在错误对话框中单击启用编辑,然后单击F5,它运行良好。
那么,我是幸运的,我的代码已经运行了几个月了,还是我错过了一些更基本的东西?
我应该如何更改此代码以避免出现这样的调试错误?更新:以下是完整的错误详细信息:
Reason: System.InvalidOperationException: Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on.
at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.Control.SetVisibleCore(Boolean value)
at System.Windows.Forms.Control.set_Visible(Boolean value)
at System.Windows.Forms.DataGridView.LayoutScrollBars()
at System.Windows.Forms.DataGridView.ComputeLayout()
at System.Windows.Forms.DataGridView.PerformLayoutPrivate(Boolean useRowShortcut, Boolean computeVisibleRows, Boolean invalidInAdjustFillingColumns, Boolean repositionEditingControl)
at System.Windows.Forms.DataGridView.ResetUIState(Boolean useRowShortcut, Boolean computeVisibleRows)
at System.Windows.Forms.DataGridViewRowCollection.OnCollectionChanged_PreNotification(CollectionChangeAction cca, Int32 rowIndex, Int32 rowCount, DataGridViewRow& dataGridViewRow, Boolean changeIsInsertion)
at System.Windows.Forms.DataGridViewRowCollection.OnCollectionChanged(CollectionChangeEventArgs e, Int32 rowIndex, Int32 rowCount, Boolean changeIsDeletion, Boolean changeIsInsertion, Boolean recreateNewRow, Point newCurrentCell)
at System.Windows.Forms.DataGridViewRowCollection.ClearInternal(Boolean recreateNewRow)
at System.Windows.Forms.DataGridView.RefreshColumnsAndRows()
at System.Windows.Forms.DataGridView.OnDataSourceChanged(EventArgs e)
at System.Windows.Forms.DataGridView.set_DataSource(Object value)
at SFDetachifier.SFDetachifier.bgProcessing_Production_DoWork(Object sender, DoWorkEventArgs e) in C:'Users'nightcopy'Documents'Visual Studio 2010'Projects'SFDetachifier_2013'SFDetachifier'SFDetachifier.cs:line 1464
at System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs e)
at System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument)
编辑:我应该提到,我使用以下代码对其他控件(如文本框(进行线程安全调用:
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.lblAccessStatus.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.lblAccessStatus.Text = text;
this.lblAccessStatus.Refresh();
}
}
那么我需要在数据网格上做类似的事情吗?
gvTaskCases.DataSource = null;
您可以从调用堆栈中判断出是这个语句导致了崩溃。它第一次工作是因为尚未设置DataSource属性。因此,取消设置不会有任何效果。但第二次影响很大,网格需要更新,因为它不再有任何数据了。Kaboom,当这种情况发生在UI线程以外的任何线程上时。
DataSource属性是而不是线程安全的。
简单的解决方法是在调用RunWorkersync((之前将其设置为null。
在控件上使用InvokeRequired:
Action task = () => {
gvTaskCases.DataSource = null;
if (gvTaskCases.Rows.Count != 0) // EXCEPTION IS THROWN HERE!
{
gvTaskCases.Rows.Clear(); // .Update();
}
};
if(gvTaskCases.InvokeRequired) {
gvTaskCases.Invoke(task);
}
else {
task();
}
这是一个与Windows窗体线程模型有关的问题。
来自MSDN:Windows窗体使用单线程单元(STA(模型,因为Windows窗体基于固有的单元线程Win32窗口。STA模型意味着窗口可以在任何线程上创建,但一旦创建,它就不能切换线程,并且对它的所有函数调用都必须在其创建线程上进行。在Windows窗体之外,中的类。NET框架使用自由线程模型。
因此,您应该使用Invoke,或者您可以使用BackgroundWorker来自动处理此类问题。