我正在运行几个用于执行查询的BackgroundWorker
个线程,以便在另一个BackgroundWorker
线程中检索DataSet。让我们调用运行这些多个线程的线程“主机线程”'和其他人查询线程'。我想要做的是通过利用主机线程的RunWorkerCompleted事件来判断所有查询线程何时完成填充其DataSet。此事件处理程序的第一行是
while (dataSets.Count < count) { Thread.Sleep(100); }
//dataSets is a Dictionary<string, DataSet>
其中count是预期返回的DataSet的总量。我的问题似乎是在填充所有数据集之前,dataSets.Count似乎变为==到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();
正如我在代码中的注释中所述 - 我知道只要在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();
});
});
答案 0 :(得分:2)
就我个人而言,我从不喜欢睡眠,因为它无法预测。如果我必须使用BackgroundWorker,那么我可能会使用IsBusy属性来确定BackgroundThread是否已完成。
使用TPL的示例代码,请注意这只是一个示例,在现实世界中,您可能希望处理异常,传递取消令牌和其他内容:)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BGToTPL
{
class Program
{
static void Main(string[] args)
{
Task[] tasks = new Task[20];
//Parent task is starting 20 child tasks
var parentTask = Task.Run(() =>
{
Console.WriteLine("Parent threadid: " + System.Threading.Thread.CurrentThread.ManagedThreadId);
for (int i = 0; i < 20; i++)
{
tasks[i] = Task.Factory.StartNew(() =>
{
Console.WriteLine("Child threadid: " + System.Threading.Thread.CurrentThread.ManagedThreadId);
Task.Delay(15000);
});
}
});
parentTask.Wait();
Console.WriteLine("Parent task has started creating and running all the child tasks, now waiting for child tasks to be over.");
//Now wait for all the tasks to be done
Task.WaitAll(tasks);
Console.WriteLine("All the tasks are done");
Console.ReadKey();
}
}
}
输出
答案 1 :(得分:1)
这样的事情怎么样? - 当然,如果没有选择TPL:
private readonly IList<BackgroundWorker> workers = new List<BackgroundWorker>();
private void Run()
{
var worker1 = new BackgroundWorker();
worker1.DoWork += (sender, args) => Thread.Sleep(1000);
worker1.RunWorkerCompleted += (sender, args) => this.CheckThreads();
var worker2 = new BackgroundWorker();
worker2.DoWork += (sender, args) => Thread.Sleep(1000);
worker2.RunWorkerCompleted += (sender, args) => this.CheckThreads();
lock (this.workers)
{
this.workers.Add(worker1);
this.workers.Add(worker2);
}
worker1.RunWorkerAsync();
worker2.RunWorkerAsync();
}
private void CheckThreads()
{
lock (this.workers)
{
if (this.workers.All(w => !w.IsBusy))
{
Console.WriteLine("All workers completed");
}
}
}