如何使用委托管理多个异步调用






3.67/5 (8投票s)
如何使用委托管理多个异步调用
引言
在 .NET 中进行异步调用有很多种方法,你应该根据不同的场景选择使用哪一种。我在这里要讨论的场景是,当你需要进行多个异步调用,并且需要等待所有调用完成后才能进行下一步操作时该如何处理。
这是一种典型的情况,你需要进行多个数据库调用,并且在继续你的流程之前等待所有结果集返回。
问题
在这个例子中,你有一个方法 SomeRandomNumber
,它需要一些时间来执行,并且会返回一个整数值。你需要对该方法进行 3 次异步调用,并等待所有调用完成。
private static int SomeRandomNumber(int waitTime)
{
Thread.Sleep(waitTime);
return rnd.Next();
}
最常见的解决方案之一是将所有异步调用放入 ThreadPool
,并且你需要一个循环来检查它们是否都已完成,基于你实例中的某个全局布尔标志。首先,代码不够清晰,并且这种命令式风格在多线程情况下难以控制。你无法确定其他人不会更改你的标志!
private int? threadOneResult;
private int? threadTwoResult;
private int? threadThreeResult;
public void AsyncCallsWithThreadPool()
{
ThreadPool.QueueUserWorkItem(ThreadOne, 1000);
ThreadPool.QueueUserWorkItem(ThreadTwo, 5000);
ThreadPool.QueueUserWorkItem(ThreadThree, 10000);
while (threadOneResult == null ||
threadTwoResult == null ||
threadThreeResult == null)
{
Thread.Sleep(100);
}
// continue
Console.WriteLine(
String.Format("Result: {0}\t{1}\t{2}",
threadOneResult,
threadTwoResult,
threadThreeResult));
}
ThreadOne
、ThreadTwo
和 ThreadThree
将结果放回 global int?
变量中。当所有三个线程完成后,上述方法将退出 while
循环。
private void ThreadOne(object state)
{
threadOneResult = SomeRandomNumber((int)state);
}
private void ThreadTwo(object state)
{
threadTwoResult = SomeRandomNumber((int)state);
}
private void ThreadThree(object state)
{
threadThreeResult = SomeRandomNumber((int)state);
}
解决方案
BeginInvoke
和 EndInvoke
方法将为你完成所有工作。BeginInvoke
将在新的线程中处理你的异步调用。EndInvoke
将从该线程返回结果。如果线程在调用 EndInvoke
时尚未完成,它将等待。你无需在主线程中实现你的 wait
循环。每个异步操作的状态都保存在 IAsyncResult
对象中,因此一个委托可以处理多个调用。
这种方法可以消除所有不必要的全局变量,因此你的异步逻辑不需要依赖于此实例中的状态。这看起来很清晰。最重要的是,这保持了你的线程结构简单。
public void AsyncCallsInDelegate()
{
GetNumber deleg = new GetNumber(SomeRandomNumber);
IAsyncResult rst1 = deleg.BeginInvoke(1000, null, null);
IAsyncResult rst2 = deleg.BeginInvoke(5000, null, null);
IAsyncResult rst3 = deleg.BeginInvoke(10000, null, null);
Console.WriteLine(
String.Format("Result: {0}\t{1}\t{2}",
deleg.EndInvoke(rst1),
deleg.EndInvoke(rst2),
deleg.EndInvoke(rst3)));
}
在 .NET 3.5 中,生活变得更好了。你可以使用泛型委托 Func<int>
来节省声明新委托的工作。
public void AsyncCallsInGenericDelege()
{
Func<int> deleg = new Func<int>(SomeRandomNumber);
IAsyncResult rst1 = deleg.BeginInvoke(1000, null, null);
IAsyncResult rst2 = deleg.BeginInvoke(5000, null, null);
IAsyncResult rst3 = deleg.BeginInvoke(10000, null, null);
Console.WriteLine(
String.Format("Result: {0}\t{1}\t{2}",
deleg.EndInvoke(rst1),
deleg.EndInvoke(rst2),
deleg.EndInvoke(rst3)));
}
历史
- 2010 年 8 月 8 日 - CodeProject 的首次发布