Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Asynchronous file IO using anonymous methods

2.89/5 (10 votes)
15 Aug 2006CPOL3 min read 1   561  
An article on how to do async file IO in C# using anonymous methods.

Introduction

In this article, I will explain how to write code in C# to achieve asynchronous file IO using anonymous methods.

Using the code

The sample code with this article can be used like a template; just fill in the sections as per your requirement and use it.

Here is some explanation of the code:

I have started off by defining some variables:

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

Notice that I have used a little cryptic way to read a file. Normally, you would use StreamReader, since is is optimised to read text. However, StreamReader does not support asynchronous read, so we have to use FileStream to read data. With FileStream, data is read in a byte[] and it can be easily converted to a string, as shown below:

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

Next, we do the actual asynchronous read. Any class that supports async operations would have methods like Beginxxx and Endxxx.

I have passed:

  • byte[] data -- which will hold the data after it is read,
  • 0 -- to indicate that the read should begin from the start of the file,
  • data.Length -- the number of bytes I want to read from the file

    (Remember, if the file is Unicode, then 1 char == 2 bytes, also converting it to string will need a different encoding.)

  • a delegate -- which will actually do the file read, and finally
  • null -- since I don't want to save any state. Why? I have explained after the formal definition.

BeginRead in FileStream is formally defined as:

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

Summary:

Begins an asynchronous read.

Parameters:

  • offset: The byte offset in array at which to begin reading.
  • array: The buffer to read data into.
  • numBytes: The maximum number of bytes to read.
  • userCallback: The method to be called when the asynchronous read operation is completed.
  • stateObject: A user-provided object that distinguishes this particular asynchronous read request from other requests.

Returns:

A System.IAsyncResult that references the asynchronous read.

Exceptions:

  • System.ArgumentNullException: array is null.
  • System.ArgumentOutOfRangeException: offset or numBytes is negative.
  • System.ArgumentException: The array length minus offset is less than numBytes.
  • System.IO.IOException: An asynchronous read was attempted past the end of the file.

There are two ways of doing this call: one is the way I have written, and the other way is by explicitly writing a function which will do the read, in which case, we have to pass the FileStream object as the stateObject - the last argument.

I have implemented an anonymous method to get rid of the extra function, which also gets rid of all the trouble to save a state and passing it to the actual function.

C#
// 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);

All objects are already accessible since we are in the same function. We just use it in the EndRead method.

After that I have converted byte[] data to a string using ASCIIEncoding, and finally, just to demonstrate that section 1 and section 2 were running in parallel, the timestamp is shown in the console.

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

Finally, section 2 follows after the BeginRead call. Here again, we just write the stopwatch value and wait for user input. Remember that we have two managed threads running, so if we close the program after running section 2, we might not give enough time to section 1 to complete.

Points of interest

Although the code is pretty simple, its correct usage is not so easy, your project has to be architected such that it can exploit the feature of asynchronous programming.

History

  • Submitted this article: 15/Aug/2006.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)