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:
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:
fs.BeginRead
(data,
0,
data.Length,
delegate(IAsyncResult ar) { },
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:
public override System.IAsyncResult BeginRead(byte[] array, int offset, int numBytes,
System.AsyncCallback userCallback, object stateObject)
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.
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.
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.