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

本文深入讲解了.NET Framework中异步读写操作的实现原理及应用,包括如何利用BeginRead/EndRead与BeginWrite/EndWrite进行高效文件处理,并探讨了匿名方法与lambda表达式的运用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

异步的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

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

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

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

异步调用

Code1.2

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

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

方法是使用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

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

29ExpandedBlockEnd.gif    }


对比
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

ContractedBlock.gif ExpandedBlockStart.gif Code
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

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

21ExpandedBlockEnd.gif    }


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

转载于:https://www.cnblogs.com/30ErLi/archive/2010/09/19/1830730.html

在这个的基础上实现<template> <div class="container"> <!-- 第一列:输入文本框和生成语音按钮 --> <div class="column"> <el-input v-model="text" type="textarea" :rows="20" placeholder="输入要转换的文本或上传TXT文件" /> <div class="file-upload mt-10"> <input type="file" accept=".txt" ref="fileInput" style="display: none" @change="handleFileUpload" /> <el-button type="primary" class="mt-20" @click="triggerFileUpload">上传TXT文件</el-button> <el-button type="primary" class="mt-20" @click="generateSpeech">生成语音文件</el-button> <el-button type="success" class="mt-20" @click="downloadAudio" :disabled="!audioBlob">下载语音文件</el-button> </div> </div> <!-- 第二列:语言选择器、语音选择器和滑块 --> <div class="column"> <el-select v-model="selectedLanguage" placeholder="选择语言" class="mt-20" @change="filterVoices" > <el-option v-for="lang in languages" :key="lang.value" :label="lang.label" :value="lang.value" /> </el-select> <el-select v-model="selectedFormat" placeholder="选择音频格式" class="mt-20" > <el-option v-for="format in audioFormats" :key="format.value" :label="format.label" :value="format.value" /> </el-select> <div class="sliders mt-20"> <div class="slider-item"> <span>语速 ({{ rate }}x)</span> <el-slider v-model="rate" :min="0.5" :max="2" :step="0.1" /> </div> <div class="slider-item"> <span>音调 ({{ pitch }})</span> <el-slider v-model="pitch" :min="0" :max="2" :step="0.1" /> </div> <div class="slider-item"> <span>音量 ({{ volume }})</span> <el-slider v-model="volume" :min="0" :max="1" :step="0.1" /> </div> </div>
最新发布
03-09
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值