65.9K
CodeProject 正在变化。 阅读更多。
Home

使用匿名方法进行异步文件 IO

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.89/5 (10投票s)

2006 年 8 月 15 日

CPOL

3分钟阅读

viewsIcon

46866

downloadIcon

575

一篇关于如何使用匿名方法在 C# 中进行异步文件 IO 的文章。

引言

在本文中,我将解释如何使用匿名方法在 C# 中编写代码以实现异步文件 IO。

使用代码

本文中的示例代码可以用作模板;只需根据您的需求填写各个部分并使用它即可。

以下是代码的一些解释

我首先定义了一些变量

FileStream fs = new FileStream("C:\\sample.txt",FileMode.Open); 
Byte[] data = new byte[200]; 
long timeNow = Stopwatch.GetTimestamp();

请注意,我使用了一种稍微隐晦的方式来读取文件。通常,您会使用 StreamReader,因为它经过优化以读取文本。但是,StreamReader 不支持异步读取,因此我们必须使用 FileStream 来读取数据。使用 FileStream,数据以 byte[] 的形式读取,并且可以轻松转换为 string,如下所示

fs.BeginRead
(data,
0,
data.Length,
delegate(IAsyncResult ar) { /* Section 1...some code*/ },
null);

接下来,我们进行实际的异步读取。任何支持异步操作的类都会有类似 BeginxxxEndxxx 的方法。

我已传递

  • byte[] data -- 它将保存读取后的数据,
  • 0 -- 以表明读取应从文件的开头开始,
  • data.Length -- 我要从文件中读取的字节数

    (请记住,如果文件是 Unicode 格式,则 1 个字符 == 2 个字节,将其转换为字符串也需要不同的编码。)

  • 一个委托 -- 它将实际进行文件读取,最后
  • null -- 因为我不想保存任何状态。为什么?我在正式定义之后解释过。

FileStream 中的 BeginRead 的正式定义为

public override System.IAsyncResult BeginRead(byte[] array, int offset, int numBytes, 
       System.AsyncCallback userCallback, object stateObject)
//Member of System.IO.FileStream

摘要

开始异步读取。

参数

  • offset:开始读取的数组中的字节偏移量。
  • array:将数据读取到的缓冲区。
  • numBytes:要读取的最大字节数。
  • userCallback:异步读取操作完成后要调用的方法。
  • stateObject:用户提供的对象,用于将此特定的异步读取请求与其他请求区分开来。

Returns

一个 System.IAsyncResult,它引用异步读取。

异常

  • System.ArgumentNullException:array 为 null。
  • System.ArgumentOutOfRangeExceptionoffsetnumBytes 为负数。
  • System.ArgumentException:数组长度减去偏移量小于 numBytes
  • System.IO.IOException:尝试在文件末尾之后进行异步读取。

有两种方法可以进行此调用:一种是我编写的方式,另一种方式是显式编写一个将进行读取的函数,在这种情况下,我们必须将 FileStream 对象作为 stateObject 传递 - 最后一个参数。

我实现了一个匿名方法来摆脱额外的函数,这也摆脱了保存状态并将其传递给实际函数的所有麻烦。

// Section 1 
// =========    
int bytesRead = fs.EndRead(ar);
fs.Close();
Console.WriteLine(ASCIIEncoding.ASCII.GetString(data));
Console.WriteLine("Time stamp to end section 1 : {0}", 
                  Stopwatch.GetTimestamp()-timeNow);

由于我们在同一个函数中,因此所有对象都已可访问。我们仅在 EndRead 方法中使用它。

之后,我使用 ASCIIEncodingbyte[] data 转换为 string,最后,为了证明第 1 节和第 2 节是并行运行的,时间戳显示在控制台中。

// Section 2
// =========
Console.WriteLine("Time stamp to end section 2 : {0}", 
                  Stopwatch.GetTimestamp()-timeNow);
Console.ReadLine();

最后,第 2 节紧跟在 BeginRead 调用之后。在这里,我们再次只写入秒表值并等待用户输入。请记住,我们有两个托管线程正在运行,因此如果我们在运行第 2 节之后关闭程序,我们可能没有足够的时间让第 1 节完成。

关注点

尽管代码非常简单,但它的正确用法却并非易事,您的项目必须进行架构设计,以便它可以利用异步编程的特性。

历史

  • 提交此文章:2006 年 8 月 15 日。
© . All rights reserved.