目录
使用 AsyncEnumerator 类
AsyncEnumerator 的体系结构
使用 Wait 和 Inbox 计数器
线程安全变形
了解更多信息
在上期专栏 (并发事件: 通过 C# 简化 APM) 中,,我讲述了一些有关如何使用新的 C# 语言功能( 匿名方法、lambda 表达式和迭代器)简化异步编程的理念。在专栏最后,我说明了如何借助 C# 迭代器 使用同步编程模型完成异步编程。图 1 显示了示例迭代器。不过,仅使用常规的 C# foreach 语句是无 法执行迭代器的代码的,因为此代码始终由调用 foreach 的线程执行,并且是连续执行,不会暂停来完 成异步 I/O 操作。
在本期专栏中,我将介绍我的 AsyncEnumerator 类,它可以智能地驱动迭代器,使不同的线程池线程 能够在不同的时间执行代码,我可以确保迭代器只有在完成异步 I/O 操作后才会执行下一次迭代。此外 ,我还会介绍 AsyncEnumerator 类的体系结构及其工作原理。
使用 AsyncEnumerator 类
下面是 AsyncEnumerator 类的定义:
public class AsyncEnumerator {
// Methods called by code outside of the iterator
public AsyncEnumerator();
public void Execute(IEnumerator<Int32> enumerator);
// Methods called by code inside the iterator
public AsyncCallback End();
public IAsyncResult DequeueAsyncResult();
}
AsyncEnumerator 类的用法相当简单。首先,我们来介绍如何实现您的迭代器成员,然后再介绍如何 调用。
将迭代器成员定义为可以接受所需的任何参数,并添加一个附加参数,该参数是对 AsyncEnumerator 对象的引用。返回 Int32 集合时您的迭代器成员必须设为原型;换句话说,它的返回类型必须是 IEnumerator<Int32>。
然后,在迭代器成员内部,通过调用相应的 BeginXxx 方法启动每个异步操作。您知道,如果调用 BeginXxx 方法,则必须将完成异步操作时应调用的方法的名称传递给它。
您可以调用 AsyncEnumerator 对象的 End 方法,而不需要定义自己的方法。End 方法会返回一个 AsyncCallback 代理,此代理标识在 AsyncEnumerator 类中定义的私有方法。因此,每个异步操作完成 时,AsyncEnumerator 对象中的代码都会得到通知。然后,此代码会将已完成的操作的 IAsyncResult 对 象放到 List<IAsyncResult> 对象(我称它为 inbox,即收件箱)中。
在您的代码中,在调用 BeginXxx 方法之后,放置一个 yield return 语句,用于返回排队等候的异 步操作数。在图 1 中,因为我只调用了一个 BeginXxx 方法 (BeginRead),所以 yield return 语句返 回值 1。yield return 语句将暂停您的迭代器方法,并停止执行其中的代码。
图 1 C# 迭代器
private static IEnumerator<Int32> ApmPatternWithIterator(
AsyncEnumerator ae, String pathname) {
using (FileStream fs = new FileStream(pathname, FileMode.Open,
FileAccess.Read, FileShare.Read, 8192, FileOptions.Asynchronous)) {
Byte[] data = new Byte[fs.Length];
fs.BeginRead(data, 0, data.Length, ae.End(), null);
yield return 1;
Int32 bytesRead = fs.EndRead(ae.DequeueAsyncResult());
ProcessData(data);
}
}
稍后,我会提供一些让 yield return 语句返回非 1 值的其他示例,但对于许多应用程序而言,还是 返回 1 比较合适。在 yield return 语句中指定的数字用于告知 AsyncEnumerator 对象需要完成多少个 异步操作才能恢复迭代器的执行。因此,当迭代器暂停时,您启动的所有异步操作都会继续完成。完成每 个异步操作后,AsyncEnumerator 的收件箱中都会增加一个条目。当收件箱中的项目数等于您在 yield return 语句中指定的数字时,AsyncEnumerator 对象将恢复迭代器的执行,并允许继续执行其代码。