【发布时间】:2015-10-30 16:57:59
【问题描述】:
我正在运行几个BackgroundWorkerthreads,它们用于执行查询以在另一个BackgroundWorker 线程中检索所有数据集。让我们将运行这些多个线程的线程称为“主机线程”,将其他线程称为“查询线程”。我要做的是通过利用主机线程的 RunWorkerCompleted 事件来判断所有查询线程何时完成填充其数据集。此事件处理程序的第一行是
while (dataSets.Count < count) { Thread.Sleep(100); }
//dataSets is a Dictionary<string, DataSet>
其中 count 是预期返回的 DataSet 总数。我的问题似乎是 dataSets.Count 在填充所有 DataSet 之前似乎变成了 == 到 count。
这是我的完整代码(无用/敏感信息已删除)
var hostThread = new BackgroundWorker();
hostThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(queryWorker_RunWorkerCompleted);
hostThread.DoWork += (send, even) =>
{
foreach (var cs in _connectionStrings)
{
var queryThread = new BackgroundWorker();
queryThread.DoWork += (se, eve) =>
{
var set = DataHandlers.TryGetDataSet(_query, cs, domain, username, pass);
dataSets.Add(((DataRow)set.Tables[0].Rows[0]).ItemArray[0].ToString(), set);
};
queryThread.RunWorkerAsync();
}
};
hostThread.RunWorkerAsync();
RunWorkerCompleted:
var bw = new BackgroundWorker();
bw.DoWork += (s, ev) =>
{
//Waiting for all DataSets to get populated
while (dataSets.Count < count) { Thread.Sleep(100); }
//Thread.Sleep(5000); If I add this, everything works fine, but when I start running more queries in each query thread this needs to be increased.
this.Invoke((MethodInvoker)delegate()
{
this.Cursor = Cursors.Default;
this.Hide();
foreach (var set in dataSets)
{
if (set == null)
break;
//THIS BLOCK IS NEVER HIT IF I LEAVE OUT THE FIVE SECOND SLEEP
var workflowList = new List<string>();
foreach (var row in set.Value.Tables[0].Rows)
{
workflowList.Add(((DataRow)row).ItemArray[_licensed ? 1 : 0].ToString());
}
((MainForm)this.OwnedForms[0]).ClientWorkflows = new KeyValuePair<string, List<string>>(set.Key, workflowList);
}
//This gets hit before setting properties on a child form because it still thinks there are no DataSets in the dataSets dictionary
((MainForm)this.OwnedForms[0]).ShowDialog();
this.Close();
});
};
bw.RunWorkerAsync();
所以正如我在代码中的 cmets 中所述 - 我知道,只要我在 while 循环之后添加足够长的睡眠时间,DataSet 就会在某些时候有效。那么在主机线程完成事件处理程序中判断所有查询线程何时实际完成的最佳方法是什么?
编辑:根据@ndd,这是我最终使用的。
var queryTasks = new List<Task>();
var parentTask = Task.Factory.StartNew(() =>
{
foreach (var cs in appConfigStrings)
{
queryTasks.Add(Task.Factory.StartNew(() => GetDataSets(mainForm, cs.Key, cs.Value)));
}
var array = queryTasks.ToArray();
Task.WaitAll(array);
});
parentTask.ContinueWith((t) =>
{
this.Invoke((MethodInvoker)delegate()
{
this.Cursor = Cursors.Default;
this.Hide();
foreach (var set in dataSets)
{
var workflowList = new List<string>();
foreach (var row in set.Value.Tables[0].Rows)
{
workflowList.Add(((DataRow)row).ItemArray[_licensed ? 1 : 0].ToString());
}
((MainForm)this.OwnedForms[0]).ClientWorkflows = new KeyValuePair<string, List<string>>(set.Key, workflowList);
}
((MainForm)this.OwnedForms[0]).ShowDialog();
this.Close();
});
});
【问题讨论】:
-
是否使用TPL 选项?
-
哇,我还没有被介绍到任务并行性。看了一会儿,我就知道这绝对是我最好的选择。我仍然对为什么我的解决方案无法工作感到困惑。编辑:实际上我仅限于.net fw 4.0。该死! @ndd
-
您不能只等待一些时间,这可能会花费太长时间,并且如果其中一个
BackgroundWorkers遇到异常,它将永远不会返回其数据集。您需要为每个查询线程提供一种方法来告诉主机线程它们已完成。一种方法是对它在完成时设置的每个查询线程使用AutoResetEvent,并让主机线程等待所有这些。我也不知道 TPL,所以这对我来说是显而易见的方法。
标签: c# .net multithreading winforms backgroundworker