委托的异步调用
Asynchronous calls with delegate
我目前正在使用委托在 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 之前;
也许我在那里遗漏了一些非常简单的东西?....有人可以根据我的情况给我看一个完整的工作样本吗?
你有几个选择。 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,它为等待任务提供了更好的编程抽象。
其他一些需要指出的事情:
- 请勿使用 Thread.Sleep - 可以使用它来测试您的代码,但一旦您确认您的异步代码有效,就不要使用它!
- 不要依赖委托 BeginInvoke - 这不是真正的并行性。它只是推迟了方法的调用,但它并没有按照您的想法进行。相反,如果您想并行执行这些方法,请使用 Task、ThreadPool 或 Thread。
更新
您也可以使用 TPL 并行 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();
我目前正在使用委托在 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 之前;
也许我在那里遗漏了一些非常简单的东西?....有人可以根据我的情况给我看一个完整的工作样本吗?
你有几个选择。 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,它为等待任务提供了更好的编程抽象。
其他一些需要指出的事情:
- 请勿使用 Thread.Sleep - 可以使用它来测试您的代码,但一旦您确认您的异步代码有效,就不要使用它!
- 不要依赖委托 BeginInvoke - 这不是真正的并行性。它只是推迟了方法的调用,但它并没有按照您的想法进行。相反,如果您想并行执行这些方法,请使用 Task、ThreadPool 或 Thread。
更新
您也可以使用 TPL 并行 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();