• 什么是.Net的异步机制(异步Stream读/写) step 4


    在前面3篇文章,我已经介绍了异步的工作原理和相关方法和参数的应用.下面我们介绍Stream流的操作, 并使用System.IO.FileStream来揭开如何开发异步的Stream(System.IO.Stream)/写操作

     

    异步的Stream/写操作

     

    下面是继承于System.IO.Stream的类
     System.IO
    .Stream
        Microsoft.JScript.COMCharStream
        System.IO.BufferedStream
        System.IO.FileStream
        System.IO.MemoryStream
        System.IO.UnmanagedMemoryStream
        System.Security.Cryptography.CryptoStream
        System.Printing.PrintQueueStream
        System.IO.Pipes.PipeStream
        System.Data.OracleClient.OracleBFile
        System.Data.OracleClient.OracleLob
        System.IO.Compression.DeflateStream
        System.IO.Compression.GZipStream
        System.Net.Sockets.NetworkStream
        System.Net.Security.AuthenticatedStream


    System.IO.Stream中提供了异步的读/(Read/Write)行为,上面继承于System.IO.Stream的类都具有同样的异步操作行为..Net Framework框架中,微软设计师使用Begin+同步方法名/ End+同步方法名来设计异步方法的规则,基本上我们在微软MSDN看到的 BeginXXX + EndXXX都是异步的方法,并且当我们在某个类中看到BeginInvoke / EndInvoke,都是微软提供的最原始的异步方法.System.IO.Stream中表现为BeginRead+EndRead / BeginWrite/EndWrite.

    我们来看一个例子,FileStream(System.IO),Read / BeginRead+EndRead,读取文件内容,开始我们使用同步方法.

    同步调用

    Code1.1

    复制代码
     1static class Program
     2    {
     3        static string path = @"c:\file.txt";//确保你本地有这个文件
     4        const int bufferSize = 5;//演示,一次只读取5 byte
     5        static void Main()
     6        {
     7            FileStream fs = new FileStream(path, FileMode.Open,
     8FileAccess.Read, FileShare.Read, 20480false);//同步调用false
     9            using (fs)//使用using来释放FileStream资源
    10            {
    11                byte[] data = new byte[bufferSize];
    12                StringBuilder sb = new StringBuilder(500);
    13                int byteReads;
    14                do// 不断循环,直到读取完毕
    15                {
    16                    byteReads = fs.Read(data, 0, data.Length);
    17                    sb.Append(Encoding.ASCII.GetString(data, 0, byteReads));
    18                }
     while (byteReads > 0);
    19                Console.WriteLine(sb.ToString());
    //输出到工作台
    20
    21            }
    //自动清除对象资源,隐式调用fs.Close();
    22            Console.ReadLine();// 让黑屏等待,不会直接关闭..
    23        }

    24    }
    复制代码

    方法非常简单,它会构造一个 FileStream 对象,调用 Read方法,不断循环读取数据。C# using 语句可确保完成数据处理后会关闭该 FileStream 对象。

    下面我们看异步调用(BeginRead/EndRead)

    异步调用 

    Code1.2

    复制代码
     1static class Program
     2    {
     3        static string path = @"c:\file.txt";//确保你本地有这个文件
     4        const int bufferSize = 5;//演示,一次只读取5 byte
     5        static byte[] data;
     6        static void Main()
     7        {
     8            data = new byte[bufferSize];
     9            FileStream fs = new FileStream(path, FileMode.Open,
    10FileAccess.Read, FileShare.Read, 20480, true);
    //设置异步调用true, 注意0
    11
    12            //异步读取文件,把FileStream对象作为异步的参数// <-
    13            AsyncCallback callback = new AsyncCallback(OnReadCompletion);
    14            IAsyncResult async = fs.BeginRead(data, 0, bufferSize, callback, fs); // <-
    15
    16            Console.ReadLine();// 让黑屏等待,不会直接关闭..
    17        }

    18        static void OnReadCompletion(IAsyncResult asyncResult)
    19        {
    20            FileStream fs = asyncResult.AsyncState as FileStream;
    21            int bytesRead = fs.EndRead(asyncResult);
    22            //输出到工作台
    23            Console.Write(Encoding.ASCII.GetString(data, 0, bytesRead));
    24            //不断循环,直到读取完毕
    25            if (bytesRead > 0)
    26                fs.BeginRead(data, 0, bufferSize, OnReadCompletion, fs);
    27            else
    28                fs.Close(); //当全部读取完毕,显式释放资源
    29        }

    30    }
    复制代码

    方法是使用BeginReadEndRead 完成的, 我们注意到方法不能使用 C# using 语句(释放资源),因为 FileStream 是在一个主线程中打开,然后在另一个线程中关闭的,而是通过把FileStream 作为参数的形式来在另外一个线程中关闭(fs.Close();),查看红色部分.

    注意0:创建FileStram 对象,如果没有FileStream fs = new FileStream(path, FileMode.Open,FileAccess.Read, FileShare.Read, 20480, true); bool useAsync=true 或者构造FileStream(String, FileMode, FileAccess, FileShare, Int32, FileOptions) FileOptions = FileOptions.Asynchronous 时,  即使使用了BeginRead/EndRead, 程序也是在同步执行方法,FileStream 对象会使用其他线程来模仿异步行为,反而降低了应用程序的性能.

     

    下面我将通过使用C# 匿名方法(C# 2.0)  lambda 表达式(C# 3.0引入的一个新功能) 来完成上面操作,如果对这个不熟悉的朋友可以查看下面文章.

    匿名方法:http://www.microsoft.com/china/msdn/library/langtool/vcsharp/CreElegCodAnymMeth.mspx?mfr=true

    lambda 表达式:http://msdn.microsoft.com/zh-cn/magazine/cc163362.aspx

    C# 匿名方法  lambda 表达式

    1,匿名方法:
    Code1.3

    复制代码
     1static class Program
     2    {
     3        static string path = @"c:\file.txt";//确保你本地有这个文件
     4        const int bufferSize = 5;//演示,一次只读取5 byte
     5        static void Main()
     6        {
     7            byte[] data = new byte[bufferSize];
     8            //[1]
     9            FileStream fs = new FileStream(path, FileMode.Open,
    10FileAccess.Read, FileShare.Read, 20480true);//设置异步调用true
    11            //使用匿名委托方式
    12            AsyncCallback callback = null//注意1
    13            callback = delegate(IAsyncResult asyncResult)//匿名方法
    14            {
    15                int bytesRead = fs.EndRead(asyncResult);//[2]
    16                Console.Write(Encoding.ASCII.GetString(data, 0, bytesRead));//输出到工作台
    17                //不断循环,直到读取完毕
    18                if (bytesRead > 0)
    19                    fs.BeginRead(data, 0, bufferSize, callback, null);//[3]
    20                else
    21                    fs.Close();//[4]
    22            }
    ;
    23
    24            //异步读取文件
    25            IAsyncResult async = fs.BeginRead(data, 0, bufferSize, callback, null);
    26
    27            Console.ReadLine();// 让黑屏等待,不会直接关闭..
    28        }

    29    }
    复制代码


    对比
    Code1.2代码我们可以看出, 匿名方法非常出色的完成我们功能, 在匿名方面体内 fs ([2][3][4])像普通变量一样执行引用FileStream([1]) 对象,而不需要任何的类型转换. 对象在方法之间轻松实现传递,并且从一个线程轻松迁移到另一个线程, APM 编程而言这是十分完美的,但实际上编译器会重新编写您的代码,从堆栈中取出这些变量,并将它们作为字段嵌入对象。由于编译器会自动执行所有的工作,您可以很轻松地将最后一个参数的空值传递到 BeginRead 方法,因为现在没有必要再在方法和线程之间显式传递的数据了。

    注意1: 必须先AsyncCallback callback = null; 要不编程器会告诉你错误:” Use of unassigned local variable 'callback '”.

    2,Lambda 表达式

    我们只需要简单的修改(在执行同一操作,lambda 表达式语法比 C# 匿名方法更简洁),匿名方法,Code1.3中红色部分
    callback = delegate(IAsyncResult asyncResult)

    修改成

    callback = asyncResult =>

    下面是完整代码.

    Code1.4


    static class Program
        {
            
    static string path = @"c:\file.txt";//确保你本地有这个文件
            const int bufferSize = 5;//演示,一次只读取5 byte
            static void Main()
            {
                
    byte[] data = new byte[bufferSize];

                FileStream fs 
    = new FileStream(path, FileMode.Open,
    FileAccess.Read, FileShare.Read, 
    20480true);//设置异步调用true
                
    //使用lambda 表达式
                AsyncCallback callback = null;//注意1
                callback = asyncResult =>
                {
                    
    int bytesRead = fs.EndRead(asyncResult);
                    Console.Write(Encoding.ASCII.GetString(data, 
    0, bytesRead));//输出到工作台
                    
    //不断循环,直到读取完毕
                    if (bytesRead > 0)
                        fs.BeginRead(data, 
    0, bufferSize, callback, null);
                    
    else
                        fs.Close();
                };

                
    //异步读取文件
                IAsyncResult async = fs.BeginRead(data, 0, bufferSize, callback, null);

                Console.ReadLine();
    // 让黑屏等待,不会直接关闭..
            }
        }

    最后,我们来看看异步的写操作(BeginWrite/EndWrite)

    Code2

    复制代码
     1static class Program
     2    {
     3        static void Main()
     4        {
     5            FileStream fs = new FileStream("text.txt", FileMode.Create,
     6FileAccess.ReadWrite, FileShare.None, 20480true);//设置异步调用true
     7            //输入信息
     8            Console.Write("Please Enter:");
     9            byte[] data = Encoding.ASCII.GetBytes(Console.ReadLine());
    10
    11            //异步写文件
    12            IAsyncResult async = fs.BeginWrite(data, 0, data.Length, asyncResult =>
    13            {
    14                fs.EndWrite(asyncResult);//写文件介绍,输出到text.txt文件中.
    15                fs.Close();
    16
    17            }
    null);
    18
    19            Console.ReadLine();// 让黑屏等待,不会直接关闭..
    20        }

    21    }
    复制代码


    大家觉得是否很简单呢? 基本上所有具有异步行为的流(继承于System.IO.Stream)操作都可以按照类似于上面的代码编写. 当然其他异步行为也可以使用上面代码中的技巧. 在System.IO.Stream 中,提供了ReadTimeout/WriteTimeout 的超时处理,但是基类中是不支持的.会报 InvalidOperationException 异常,反编译可以看到throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_TimeoutsNotSupported")).


    下篇文章我会提供其他的例子来说明异步中的线程间通信
    .采用Window Forms程序.

    以上有word 文档直接粘贴,排版可能不太好看,你可以通过下面来下载相应的代码/文档

    1,文档

    2,代码(VS2008开发,.Net Framework 3.5(C Sharp)编写)

  • 相关阅读:
    建站随手记:about server stack
    【个人申明】主要发表平台迁至简书和公众号
    每日一问:Android 消息机制,我有必要再讲一次!
    每日一问:View.getContext() 的返回一定是 Activity 么?
    每日一问:Android 滑动冲突,你们都是怎样处理的
    每日一问:谈谈 SharedPreferences 的 apply() 和 commit()
    每日一问:浅谈 onAttachedToWindow 和 onDetachedFromWindow
    每日一问:到底为什么属性动画后 View 在新位置还能响应事件
    百万级日活 App 的屏幕录制功能是如何实现的
    每日一问:不一样的角度吐槽下 DataBinding
  • 原文地址:https://www.cnblogs.com/ywsoftware/p/3105405.html
Copyright © 2020-2023  润新知