APM(异步编程模型)聚集技巧之等待直至完成聚集技巧

APM(异步编程模型)支持三种聚集技巧:等待直至完成,轮询和方法回调。

下面我们先来看看等待直至完成聚集(wait-until-done)技巧:

         我们为了启动一个异步操作,我们可以调用一些BeginXXXfangfa 。所有这些方法都会将请求操作排队,然后返回一个IAsyncResult对象来标识挂起的操作。为了获取操作的结果,我们可以以IAsyncResult对象为参数调用相应的EndXXX方法。根据记录,所有的EndXXX方法都可以接受一个IAsyncResult作为它的一个参数。我们可以看一个EndRead方法:

Int32 EndRead(IAsyncResult asyncResult)

注意:EndRead方法的唯一一个参数就是IAsyncResult。但是,更重要的一点就是EndRead方法的返回值是Int32类型,与Read方法的返回值相同。当EndRead方法返回时,它返回从FileStream对象中所读取的字节的数量。

         下面,我们来看一个小例:

public static void Main(string[] args)

       {

           //打开指示异步I/O操作的文件

           FileStream fs = new FileStream(@"C:\Boot.ini", FileMode.Open,FileAccess.Read, FileShare.Read, 1024, FileOptions.Asynchronous);

           Byte[] data = new Byte[100];

 

           //FileStream对象初始化一个异步读操作

           IAsyncResult ar =fs.BeginRead(data, 0, data.Length, null, null);

 

           // 在这里可以执行一些代码,看个人需求,稍后会分析为什么

           // 挂起该线程直至异步操作结束并获得结果

           Int32 bytesRead = fs.EndRead(ar);

           fs.Close();

 

           Console.WriteLine("字节数是:{0}", bytesRead);

           Console.WriteLine(BitConverter.ToString(data, 0, bytesRead));

           Console.ReadLine();

       }

 

我在自己的机器上得到的结果:

字节数是:82

20-0D-0A-43-3A-5C-47-52-4C-44-52-3D-D2-BB-BC-FC-BB-B9-D4-AD-20-47-68-6F-73-74-20-76-31-31-2E-30-20-0D-0A-5B-62-6F-6F-74-20-6C-6F-61-64-65-72-5D-0D-0A-74-69-6D-65-6F-75-74-3D-33-0D-0A-5B-6F-70-65-72-61-74-69-6E-67-20-73-79-73-74-65-6D-73-5D-0D-0A

 

如代码所示,该程序没有有效地利用APM。该程序在调用一个BeginXXX方法之后立即调用了一个EndXXX方法,这样做有点笨,因为调用线程进入了睡眠状态,在等待操作的完成,如果希望异步执行该操作,可以调用一个Read方法,这样做效率更高。

         但是在BeginXXXEndXXX方法之间放入一些代码,会看到APM的一些价值,因为这些代码可以在读取文件字节的过程中执行。下面的代码对前面的程序进行了实质性的修改。新版本的程序可以同时执行从多个流中读取数据。在这个例子中,使用FileStream对象,但是该代码可以在所有的派生至Stream的对象上工作,因此它可以从多个文件、套接字甚至串口中同时读取字节。为了支持这些功能,我们必须使用自己编写的AsyncStreamRead类,并将指定的参数传递给AsyncStreamRead的构造器。

       private static void ReadMultipleFiles(params String[] pathnames)

       {

           AsyncStreamRead[] asrs = new AsyncStreamRead[pathnames.Length];

           for (Int32 n = 0; n < pathnames.Length; n++)

           {

                Stream stream = newFileStream(pathnames[n], FileMode.Open, FileAccess.Read, FileShare.Read, 1024,FileOptions.Asynchronous);

 

                asrs[n] = new AsyncStreamRead(stream,100);

           }

           for (Int32 n = 0; n < asrs.Length; n++)

           {

                Byte[] bytesRead =asrs[n].EndRead();

 

                Console.WriteLine("读取的字节数为:{0}",bytesRead.Length);

                Console.WriteLine(BitConverter.ToString(bytesRead));

           }

       }

 

       private sealed class AsyncStreamRead

       {

           private Stream m_stream;

           private IAsyncResult m_ar;

           private Byte[] m_data;

           public AsyncStreamRead(Stream stream, Int32 numBytes)

           {

                m_stream = stream;

                m_data = new Byte[numBytes];

 

                //Stream对象初始化一个异步读操作

                m_ar = stream.BeginRead(m_data,0, numBytes, null, null);

           }

 

           public Byte[] EndRead()

           {

                Int32 numBytesRead =m_stream.EndRead(m_ar);

                m_stream.Close();

                Array.Resize(ref m_data,numBytesRead);

 

                return m_data;

           }

       }

    上述代码同时执行所有的读操作时,将非常的高效,但该代码还存在一些效率低下的地方。在将所有的读请求排队后,ReadMultipleFiles方法进入第二轮循环,在这轮循环中,ReadMultipleFiles方法按照请求生成的次序为每个流调用EndRead方法。这种方式效率不高,因为不同的流需要不同的时间来读取数据。因此,有可能第二个流中的数据会在第一个流中的数据之前被读取完成。理想情况下,如果发生了这种情况,我们希望首先处理第二个流中的数据,然后在第一个流中的数据读取完成时再处理它。APM确实允许我们实现这一点,但没有采用此处讨论的等待直至完成聚集技巧。

  我的个人官网: www.shuonar.com

原文地址:https://www.cnblogs.com/shuonar/p/3118277.html