Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/270.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 对控件进行线程安全调用_C#_.net_Multithreading_Thread Safety_Backgroundworker - Fatal编程技术网

C# 对控件进行线程安全调用

C# 对控件进行线程安全调用,c#,.net,multithreading,thread-safety,backgroundworker,C#,.net,Multithreading,Thread Safety,Backgroundworker,这很奇怪 我有一个后台工作人员在我的Windows窗体上做一些工作。 作为这项工作的一部分,将更新dat网格控件 一旦过程完成,一切都很好 如果我再次单击该按钮以启动后台工作程序并再次启动该过程,我会在下面的代码中得到一个错误交叉线程无效: private void bgProcessing_Production_DoWork(object sender, DoWorkEventArgs e) { String[] args = (String[])e.Argument;

这很奇怪

我有一个后台工作人员在我的Windows窗体上做一些工作。 作为这项工作的一部分,将更新dat网格控件

一旦过程完成,一切都很好

如果我再次单击该按钮以启动后台工作程序并再次启动该过程,我会在下面的代码中得到一个错误
交叉线程无效

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();
    }
     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();
            }
        }
现在是这样的,正如我所说的,它第一次运行良好

但即使是wierder,如果我在错误对话框中单击启用编辑,然后点击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 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();
    }
     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();
            }
        }

那么我需要在数据网格上执行类似的操作吗?

使用控件上所需的invoker:

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

使用控件上所需的invokererequired:

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窗体线程模型的问题

发件人:Windows窗体使用单线程单元(STA)模型,因为Windows窗体基于固有单元线程的本机Win32窗口。STA模型意味着可以在任何线程上创建窗口,但一旦创建,它就不能切换线程,对它的所有函数调用都必须在其创建线程上进行。在Windows窗体之外,.NET Framework中的类使用自由线程模型


因此,您应该使用Invoke,或者您可以使用自动处理此类问题。

这是有关Windows窗体线程模型的问题

   gvTaskCases.DataSource = null;
发件人:Windows窗体使用单线程单元(STA)模型,因为Windows窗体基于固有单元线程的本机Win32窗口。STA模型意味着可以在任何线程上创建窗口,但一旦创建,它就不能切换线程,对它的所有函数调用都必须在其创建线程上进行。在Windows窗体之外,.NET Framework中的类使用自由线程模型

所以您应该使用Invoke,或者您可以使用来自动处理此类问题

   gvTaskCases.DataSource = null;
您可以从调用堆栈中判断是该语句导致了崩溃。它第一次起作用是因为尚未设置DataSource属性。因此,使其不稳定不会产生任何效果。但是第二次大的影响是,网格需要更新,因为它不再有任何数据。当这种情况发生在UI线程以外的任何线程上时

数据源属性不是线程安全的

简单的解决方法是在调用RunWorkerAsync()之前将其设置为null

您可以从调用堆栈中判断是该语句导致了崩溃。它第一次起作用是因为尚未设置DataSource属性。因此,使其不稳定不会产生任何效果。但是第二次大的影响是,网格需要更新,因为它不再有任何数据。当这种情况发生在UI线程以外的任何线程上时

数据源属性不是线程安全的


简单的解决方法是在调用RunWorkerAsync()之前将其设置为null。

听起来好像Visual Studio在某种“沙盒模式”下运行代码,其中Winform上的控件是只读的。如果您告诉我们异常是什么,可能会有所帮助。您不应该尝试在后台工作人员执行的操作中更新UI控件。在
RunWorkerCompleted
处理程序中执行任何更新,因为该处理程序是在UI线程上运行的。@RobertHarvey:我已经用完整的错误详细信息和一些其他信息更新了我的问题。谢谢您的帮助。@Lee我也会在
RunWorkerCompleted
处理程序中进行更新,但是在这种情况下,如果进程再次运行,我需要清除
DoWork
中的网格(例如,使用不同的日期参数…是的,您很幸运。WinForms控件在某些关键点进行交叉线程检查,但可以逃避这些检查。这并不意味着您的代码是安全的。对所有GUI访问都使用Invoke()。听起来Visual Studio在某种“沙盒模式”下运行您的代码,”Winform上的控件是只读的。如果您告诉我们异常是什么,可能会有所帮助。您不应该在后台工作人员执行的操作中尝试更新UI控件。请在
RunWorkerCompleted
处理程序中执行任何更新,因为它是在UI线程上运行的。@RobertHarvey:我已经用完整的代码更新了我的问题错误详细信息和一些详细信息。谢谢您的帮助。@Lee我也会在
RunWorkerCompleted
处理程序中进行更新,但是在这种情况下,如果进程再次运行,我需要清除
DoWork
中的网格(例如,使用不同的日期参数…是的,您很幸运。WinForms控件在某些关键点进行交叉线程检查,但可以逃避这些检查。这并不意味着您的代码是安全的。对所有GUI访问都使用Invoke()。@cashinton:我在尝试代码时遇到编译错误,例如
无效表达式项”)”
无效表达式项”(“
;预期的
{
预期的感谢如此之多,我也采纳了HAns的建议,调用了
gvTaskCases.DataSource=null;
在调用
RunWorkerAsync
之前,然后使用您的代码(没有该调用)在我的
DoWork
处理程序中!@cashinton:我在尝试您的代码时遇到编译错误,例如
无效表达式项“
无效表达式项”(“
;应为
{
expectedThanks尽管如此,我也采纳了HAns的建议,在调用
Run之前调用了
gvTaskCases.DataSource=null;