我目前正在使用委托在for循环中进行异步调用,我遇到的问题是 - 我怎么知道这些异步调用何时完成?
例如:
public delegate string GetMergeSectionCaller(string something1, out int threadId);
public Dataset GetDataset (param1, param2) {
int threadId;
Dataset ds = new Dataset;
using (myConnection) {
myConnection.StartConnection();
GetMergeSectionCaller caller = new GetMergeSectionCaller(GetMergeSection);
foreach (var r in anObjectList) {
IAsyncResult result = caller.BeginInvoke(r.ToString(), out threadId, null, null);
}
//I want to do here is wait for the above every single foreach BeginInvoke to finish; then do the below job
ds = GetFinalData();
}
//do more thing to ds here;
return ds;
}
public void GetMergeSectionCaller(string something1, out int threadId) {
//doing superlong long job
//in my actual case, it's actually inserting data to db etc
Thread.Sleep(5000);
threadId = Thread.CurrentThread.ManagedThreadId;
}
所以我尝试了不同的方法;比如将回调传递给我的BeginInvoke和EndInvoke,但是仍然 - 我错过了一种方法来阻止剩下的代码在我完成foreach之前运行;
也许我错过了一些非常简单的东西?....有人可以根据我的情况向我展示一份完整的工作样本吗?
答案 0 :(得分:2)
您有几个选择。 IAsyncResult具有WaitHandle属性,您可以使用该属性等待。
var results = new List<WaitHandle>();
foreach (var r in anObjectList) {
results.Add(caller.BeginInvoke(r.ToString(), out threadId, null, null).WaitHandle);
}
// wait for all results to complete
WaitHandle.WaitAll(results.ToArray());
另一种选择是创建一个ManualResetEvent和一个计数器,并在计数器达到0时从回调中重置事件。这种方法的优点是你只能创建一个可等待的对象,但你也必须管理柜台。
最后,另一种选择是使用新的Task-based API,它为等待任务提供了更好的编程抽象。
要指出的其他一些事情:
更新
您也可以使用TPL parallel for循环,这可能更接近您原来希望实现的目标:
Parallel.ForEach(anObjectList, anObjectItem => {
// do something with anObjectItem
});
// this parallelizes the for-loop iterations
更新2
以下是如何使用ThreadPool和ManualResetEvent中的工作线程运行任务。
ManualResetEvent mreComplete = new ManualResetEvent(false);
int callsRemaining;
GetMergeSectionCaller caller = new GetMergeSectionCaller(GetMergeSection);
callsRemaining = anObjectList.Count;
mreComplete.Reset();
foreach (var r in anObjectList) {
ThreadPool.QueueUserWorkItem((Action)delegate {
caller(r.ToString());
lock{
if(--callsRemaining==0) mreComplete.Set();
}
}
}
mreComplete.Wait();