我正在尝试在从sql server查询的网格上显示一些信息。数据收集大约需要10秒钟,因此我不想锁定UI线程。
我目前的代码如下:
ThreadPool.QueueUserWorkItem(DataUpdateThread, new UpdateParams(year));
private struct UpdateParams
{
internal string year;
internal UpdateParams(string year)
{
this.year = year;
}
}
private void DataUpdateThread(object state)
{
DataTable dTable = new DataTable();
try
{
this.Invoke((MethodInvoker)delegate
{
//stop data editing on the grid
//scrolling marquee for user
marquee.Visible = true;
marquee.Enabled = true;
grdMain.Visible = false;
grdMain.DataSource = null;
});
UpdateParams parameters = (UpdateParams)state;
dTable = GetData(parameters.year);
}
catch (Exception ex)
{
this.Invoke((MethodInvoker)delegate
{
//log error + end user message
});
}
finally
{
this.Invoke((MethodInvoker)delegate
{
grdMain.DataSource = dTable;
grdMainLevel1.RefreshData();
marquee.Visible = false;
marquee.Enabled = false;
grdMain.Visible = true;
});
}
}
除了在更新完成之前关闭它所关联的表单时,它大部分时间都可以工作,它会因错误而崩溃:
在创建窗口句柄之前,无法在控件上调用Invoke或BeginInvoke。
我理解错误将是因为表单不再存在所以当finally部分尝试在UI线程上调用方法时它不能。
有没有更好的方法来做这整件事?我想我可以处理调用错误,但它看起来很乱,我想我可能错过了一个更简单的方法。
答案 0 :(得分:4)
您可以检查表单是否已关闭,如果表单已关闭,则不进行调用。 if (this.IsHandleCreated)
应该有效。然而,这仍然会产生问题,因为表格可以在支票和BeginInvoke
的通话之间关闭。然后,唯一的“完全证明”解决方案是将整个调用封装在try / catch中。
答案 1 :(得分:1)
尝试在InvokeRequired()
Invoke()
答案 2 :(得分:1)
Invoke
在幕后使用特殊的WinForms SynchronizationContext
,您可以在应用中的任意位置使用SynchronizationContext.Current
进行访问。
在Reflector 中进行一些调整后的CORRECTION:实际上Invoke通过PostMessage直接进行编组,它是在后台使用SynchronizationContext的BackgroundWorker。如果它没有窗口句柄,则抛出将抛出。
基本上,您需要在启动线程之前将其存储在变量中,例如当你仍然在你的UI线程中,并在线程的代码中使用上下文的Post
或Send
方法。这样做可以在没有窗口把手的情况下正确地整理东西。