首先,我知道我应该使用正确的线程技术(Threadpool,BeginInvoke等)来实现这一目标,但这有点超过我的头脑,并会花一些时间来阅读材料并了解它(如果您的方案有任何URL引用,请随时发布)。
在过渡期间,我使用backgroundWorker来拉取一个非常大的结果集并用它填充DatagridView。我在DoWork事件中成功创建了SortableBindingList<TEntities>
并将结果传递出去。在RunWorkerCompleted
事件中,我将SortableBindingList<TEntities>
转换并绑定到我的网格。我关注的两个主要领域如下:
1)访问私有变量。
我想将两个参数List<long>
中的一个传递给我的DoWork
事件,但根据传递给它的列表运行不同的查询。我可以通过声明一个类级别的私有布尔变量来解决这个问题。这似乎很愚蠢,但在我的DoWork
,我是否可以访问该私有变量并相应地路由查询? (我已经测试了这个,它确实有效,没有任何错误弹出)
private bool SearchEngaged = false;
private void bgw_DoWork(object sender, DoWorkEventArgs e) {
BackgroundWorker worker = sender as BackgroundWorker;
e.Result = GetTasks((List<long>)e.Argument, worker, e);
}
SortableBindingList<Task> GetTasks(List<long> argsList, BackgroundWorker worker, DoWorkEventArgs e) {
SortableBindingList<Task> sbl = null;
if (worker.CancellationPending) {
e.Cancel = true;
}
else {
if (SearchEngaged) {
sbl = DU.GetTasksByKeys(argsList);
}
else {
sbl = DU.GetTasksByDivision(argsList);
}
}
return sbl;
}
2)UI线程在RunWorkerCompleted开始时冻结。
好的,我知道我的用户界面在DoWork
事件期间有响应,'如果我不绑定,我需要+/- 2秒才能运行并返回SortableBindingList<Task>
列出到网格,但只填充它。但是,当我将其绑定到Grid时,我的UI会冻结,我在RunWorkerCompleted
事件中执行此操作。请记住,我的网格有4个图像列,我在CellFormatting
处理。此过程需要额外的8秒才能完成,在此期间,我的UI完全是非交互式的。我知道这样做的跨线程含义,但是否有任何方法可以在后台完成网格填充和格式化,或者不会导致我的UI冻结? RunWorkeCompleted看起来像这样:
private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
if (e.Cancelled) {
lblStatus.Text = "Operation was cancelled";
}
else if (e.Error != null) {
lblStatus.Text = string.Format("Error: {0}", e.Error.Message);
}
else {
SortableBindingList<Task> sblResult = (SortableBindingList<Task>)e.Result;
dgv.DataSource = sblResult;
dgv.Enabled = true;
TimeSpan Duration = DateTime.Now.TimeOfDay - DurationStart;
lblStatus.Text = string.Format("Displaying {0} {1}", sblResult.Count, "Tasks");
lblDuration.Visible = true;
lblDuration.Text = string.Format("(data retrieved in {0} seconds)", Math.Round(Duration.TotalSeconds, 2));
cmdAsyncCancel.Visible = false;
tmrProgressUpdate.Stop();
tmrProgressUpdate.Enabled = false;
pbStatus.Visible = false;
}
}
对于冗长的查询感到抱歉,但我将非常感谢您的回复!谢谢!
答案 0 :(得分:1)
您的代码似乎做得非常正确。
至于UI线程更新屏幕所需的8秒,你无能为力。请参阅我对this question的回答。
要优化用户界面部分,您可以尝试在网格或其包含的面板上调用SuspendLayout和ResumeLayout。
您还可以尝试减少数据绑定期间完成的处理量。例如:
答案 1 :(得分:1)
我认为对您的问题最简单的解决方案是使用您自己提到的DoWork
在RunWorkerCompleted
而不是Dispatcher.BeginInvoke
设置网格的数据源。像这样:
private bool SearchEngaged = false;
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
SortableBindingList<Task> sblResult = GetTasks((List<long>)e.Argument, worker, e);
BeginInvoke((Action<object>)(o => dataGridView1.DataSource = o), sblResult);
}
private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled) {
lblStatus.Text = "Operation was cancelled";
}
else if (e.Error != null) {
lblStatus.Text = string.Format("Error: {0}", e.Error.Message);
}
else
{
dgv.Enabled = true;
TimeSpan Duration = DateTime.Now.TimeOfDay - DurationStart;
lblStatus.Text = string.Format("Displaying {0} {1}", sblResult.Count, "Tasks");
lblDuration.Visible = true;
lblDuration.Text = string.Format("(data retrieved in {0} seconds)", Math.Round(Duration.TotalSeconds, 2));
cmdAsyncCancel.Visible = false;
tmrProgressUpdate.Stop();
tmrProgressUpdate.Enabled = false;
pbStatus.Visible = false;
}
}
就私人变量问题而言,我不认为你的情况会有任何问题。如果您使用某些UI事件更改它,只需将私有字段标记为volatile
。可以在此处找到volatile
关键字的文档: