Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/318.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# BackgroundWorker OnWorkCompleted引发跨线程异常_C#_Winforms_Backgroundworker_Arcgis_Multithreading - Fatal编程技术网

C# BackgroundWorker OnWorkCompleted引发跨线程异常

C# BackgroundWorker OnWorkCompleted引发跨线程异常,c#,winforms,backgroundworker,arcgis,multithreading,C#,Winforms,Backgroundworker,Arcgis,Multithreading,我有一个用于数据库分页的简单UserControl,它使用一个控制器来执行实际的DAL调用。我使用BackgroundWorker执行重载,在OnWorkCompleted事件中,我重新启用一些按钮,更改TextBox.Text属性,并为父窗体引发事件 表单A保存我的用户控件。当我单击打开表单B的某个按钮时,即使我没有在“那里”执行任何操作并将其关闭,然后尝试从数据库中引入下一页,工作线程(而不是主线程)会调用OnWorkCompleted,并抛出跨线程异常 目前,我在那里的处理程序中添加了一个

我有一个用于数据库分页的简单UserControl,它使用一个控制器来执行实际的DAL调用。我使用
BackgroundWorker
执行重载,在
OnWorkCompleted
事件中,我重新启用一些按钮,更改
TextBox.Text属性,并为父窗体引发事件

表单A保存我的用户控件。当我单击打开表单B的某个按钮时,即使我没有在“那里”执行任何操作并将其关闭,然后尝试从数据库中引入下一页,工作线程(而不是主线程)会调用
OnWorkCompleted
,并抛出跨线程异常

目前,我在那里的处理程序中添加了一个对
invokererequired
的检查,但是
OnWorkCompleted
的全部要点不是要在主线程上调用吗?为什么它不能像预期的那样工作

编辑:

我已设法将问题缩小到arcgis和
BackgroundWorker
。我有下面的解决方案,它在arcmap中添加了一个命令,可以用两个按钮打开一个简单的
Form1

第一个按钮运行一个
BackgroundWorker
,该按钮睡眠500毫秒并更新计数器。 在
RunWorkerCompleted
方法中,它检查
invokererequired
,并更新标题以显示该方法最初是在主线程还是工作线程内运行的。 第二个按钮只打开
Form2
,其中不包含任何内容

首先,对
RunWorkerComplete
的所有调用都是在主线程内进行的(正如预期的那样-这是RunWorkerComplete方法的整体要点,至少从我对
BackgroundWorker
的了解来看是如此)

在打开和关闭
Form2
之后,总是在工作线程上调用
RunWorkerCompleted
。我想补充一点,我可以将此解决方案保持原样(在
RunWorkerCompleted
方法中检查
invokererequired
),但我想了解为什么它会发生在我的预期之外。在我的“真实”代码中,我希望始终知道在主线程上调用
RunWorkerCompleted
方法

我设法将问题定位在
表单.Show()上BackgroundTesterBtn
中的code>命令-如果我改用
ShowDialog()
,我不会遇到问题(
RunWorkerCompleted
始终在主线程上运行)。我确实需要在我的ArcMap项目中使用
Show()
,这样用户就不会被绑定到表单

我还试图在一个普通的WinForms项目上重现这个bug。我添加了一个简单的项目,在没有ArcMap的情况下只打开第一个表单,但在这种情况下,我无法重现错误-无论我在打开
Form2
之前还是之后使用
Show()
还是
ShowDialog()
,都会在主线程上运行
RunWorkerCompleted
。我尝试在我的
表单1
之前添加第三个表单作为主表单,但结果没有改变

是我的简单sln(VS2005sp1)-它需要

ESRI.ArcGIS.ADF(9.2.4.1420)

ESRI.ArcGIS.ArcMapUI(9.2.3.1380)

ESRI.ArcGIS.SystemUI(9.2.3.1380)

OnWorkCompleted
的整个要点不是要在主线程上调用吗?为什么它不能像预期的那样工作

不,不是。
你不能在任何旧线程上运行任何旧东西。线程不是礼貌的对象,你可以简单地说“请运行这个”

一个更好的思维模型是一列货运火车。一旦开始,它就会偏离自己的轨道。你不能改变或阻止它。如果你想影响它,你要么等到它到达下一个火车站(例如:让它手动检查一些事件),要么让它出轨(
Thread.Abort
和CrossThread异常的后果与让火车出轨差不多……小心!)

Winforms控件某种程度上支持这种行为(它们有
Control.BeginInvoke
,它允许您在UI线程上运行任何函数),但这只起作用,因为它们在windows UI消息泵中有一个特殊的挂钩,并编写一些特殊的处理程序。按照上面的类比,他们的列车在车站检票,并定期寻找新的方向,你可以使用该设施发布你自己的方向


BackgroundWorker
是为通用而设计的(它不能绑定到windows GUI),因此它不能使用windows
控件。BeginInvoke
功能。它必须假设您的主线程是一个不可阻挡的“火车”,正在做自己的事情,因此完成的事件必须在工作线程中运行,或者根本不运行

但是,当您使用winforms时,在
OnWorkCompleted
处理程序中,您可以使用上面提到的
BeginInvoke
功能让窗口执行另一个回调。像这样:

// Assume we're running in a windows forms button click so we have access to the 
// form object in the "this" variable.
void OnButton_Click(object sender, EventArgs e )
    var b = new BackgroundWorker();
    b.DoWork += ... blah blah

    // attach an anonymous function to the completed event.
    // when this function fires in the worker thread, it will ask the form (this)
    // to execute the WorkCompleteCallback on the UI thread.
    // when the form has some spare time, it will run your function, and 
    // you can do all the stuff that you want
    b.RunWorkerCompleted += (s, e) { this.BeginInvoke(WorkCompleteCallback); }
    b.RunWorkerAsync(); // GO!
}

void WorkCompleteCallback()
{
    Button.Enabled = false;
    //other stuff that only works in the UI thread
}

在访问结果属性之前,RunWorkerCompleted事件处理程序应始终检查错误和取消属性。如果引发了异常或操作被取消,则访问Result属性会引发异常


BackgroundWorker
检查委托实例是否指向支持接口
ISynchronizeInvoke
的类。DAL层可能没有实现该接口。通常,您会在
表单
上使用
BackgroundWorker
,该表单确实支持该接口

如果您想从DAL层使用
BackgroundWorker
,并想从那里更新UI,您有三个选项:

  • 您将继续调用
    Invoke
    方法
  • 在DAL类上实现接口ISynchronizeInvoke,并手动重定向调用(只有三个方法和一个属性)
  • 在调用
    BackgroundWorker之前
    
    if(control.InvokeRequired)
      control.Invoke(Action);
    else
      Action()