Noa*_*Gal 3 c# multithreading arcgis backgroundworker winforms
我有一个简单的UserControl用于数据库分页,它使用控制器来执行实际的DAL调用.我使用a BackgroundWorker
来执行繁重的工作,并在OnWorkCompleted
事件中重新启用一些按钮,更改TextBox.Text
属性并为父表单引发事件.
表单A保存我的UserControl.当我点击打开表单B的某个按钮时,即使我没有做任何"那里"并且只是关闭它,并尝试从我的数据库中引入下一页,OnWorkCompleted
在工作线程上调用它(而不是我的主线程),并抛出一个跨线程异常.
目前我InvokeRequired
在那里的处理程序中添加了一个检查,但是不是OnWorkCompleted
要在主线程上调用的全部内容?为什么不按预期工作?
编辑:
我已经设法将问题缩小到arcgis和BackgroundWorker
.我有以下解决方案,它添加了一个命令到arcmap,打开一个简单Form1
的两个按钮.
第一个按钮运行BackgroundWorker
睡眠500毫秒并更新计数器.在该RunWorkerCompleted
方法中,它检查InvokeRequired
并更新标题,以显示该方法最初在主线程或工作线程内运行.第二个按钮刚打开Form2
,不包含任何内容.
首先,所有调用RunWorkerCompletedare
都在主线程内完成(正如预期的那样 - 这就是RunWorkerComplete方法的最后一点,至少我从MSDN上了解到的BackgroundWorker
)
打开和关闭后Form2
,RunWorkerCompleted
始终在工作线程上调用.我想补充一点,我可以将此解决方案保留原样(InvokeRequired
在RunWorkerCompleted
方法中检查),但我想了解为什么它会违背我的期望.在我的"真实"代码中,我想知道该RunWorkerCompleted
方法是在主线程上调用的.
我设法form.Show();
在我的命令中指出问题BackgroundTesterBtn
- 如果我使用ShowDialog()
,我没有问题(RunWorkerCompleted
总是在主线程上运行).我需要Show()
在我的ArcMap项目中使用,以便用户不会绑定到表单.
我还尝试在正常的WinForms项目上重现该错误.我添加了一个简单的项目,只打开没有ArcMap的第一个表单,但在这种情况下我无法重现错误 - RunWorkerCompleted
主线程上的运行,无论是我使用的Show()
还是ShowDialog()
在打开之前和之后Form2
.我尝试在我之前添加第三个表单作为主要表单Form1
,但它没有改变结果.
这是我的简单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 Control.BeginInvoke
功能.它必须假设你的主线程是一个不可阻挡的"火车"做它自己的事情,所以完成的事件必须在工作线程中运行或根本不运行.
但是,当您使用winforms时,在OnWorkCompleted
处理程序中,您可以使用上面提到的功能让Window执行另一个回调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
}
Run Code Online (Sandbox Code Playgroud)
在访问Result属性之前,RunWorkerCompleted事件处理程序应始终检查Error和Cancelled属性.如果引发异常或操作已取消,则访问Result属性会引发异常.
归档时间: |
|
查看次数: |
11559 次 |
最近记录: |