.Net的异步机制(异步Stream读/写) - step 4

来源:互联网 发布:迅雷mac会员破解版 编辑:程序博客网 时间:2024/04/29 11:35

在前面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); booluseAsync=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

Code

 

最后,我们来看看异步的写操作(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")).

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 护士资格证过期了怎么办 辞职护士执业证怎么办 网约护士怎么办手续 动车票待核验怎么办 店员不维护老板怎么办 准考证号会过期怎么办 驾校准考证丢了怎么办 科目二下大雨怎么办 考科目二下雨天怎么办 普通话总是二乙怎么办 科目二很紧张怎么办 18年科目四缺考怎么办 个人医保卡欠费怎么办 医保欠费不想交怎么办 怀化市驾考绿色通道怎么办? 签注易不能办理怎么办 网上怎么办护照和签证 意大利被偷护照怎么办 户口在学校 怎么办签证 户口换了身份证怎么办 广州在校大学生怎么办护照 民间借贷无法还怎么办 退伍档案没接收怎么办 档案被单位扣住怎么办 公积金提不出来怎么办 公积金还贷款怎么办手续 科一预约失败怎么办 科四忘记预约怎么办 我科目一缺考了怎么办? 无可选考试场地怎么办 早产儿脑部发育不好怎么办 宝宝脑部发育不好怎么办 小孩脑部发育不好怎么办 8岁儿童智力低下怎么办 儿童食物不耐受怎么办 忘记就诊卡号怎么办 nt检查预约不到怎么办 听力不好科目三怎么办 青岛公安不立案怎么办 长春驾照丢了怎么办 驾照超期一个月怎么办