Asynchronous I/O

The preceding section was concerned with using streams to perform synchronous I/O operations, in which a call to a read or write method blocks until the operation completes. This section discusses the .NET support for asynchronous I/O, which allows read and write operations to be performed without blocking. Version 1.4 of the Java platform includes support for asynchronous, or nonblocking, I/O, but the .NET model differs so much that we see little benefit in providing a comparison.

Asynchronous I/O is byte-oriented, not character-oriented. All classes derived from System.IO.Stream can be used in asynchronous I/O. Importantly, none of the reader and writer classes detailed in the preceding section can be used. To demonstrate asynchronous I/O, we’ll concentrate on using the FileStream class. Since all streams can be used for asynchronous I/O, the model laid out in this section can be applied to any of the stream classes.

At the heart of all asynchronous operations is the System.AsyncCallback delegate, which will be invoked when an asynchronous operation has completed. The signature for this delegate is a method that accepts an instance of System.IAsyncResult and returns void. The following example demonstrates reading an English transcript of the Magna Carta using asynchronous calls:

using System;
using System.IO;
using System.Text;
using System.Threading;

namespace MagnaCarta {

    class MagnaCartaDemo {
        private byte[] o_byte_arr;
        private FileStream o_stream;
        private AsyncCallback o_callback;

        MagnaCartaDemo() {
            o_byte_arr = new byte[100];
            o_stream = new FileStream("magnacarta.txt",
                FileMode.Open, FileAccess.Read,
                FileShare.Read, 100, true);

            o_callback = new AsyncCallback(readFinished);

            o_stream.BeginRead(o_byte_arr, 0,
                o_byte_arr.Length, o_callback, null);

            Console.WriteLine(">>>> Current thread is " +
                "not blocking!");
        }

        private void readFinished(IAsyncResult p_result) {
            int x_bytes_read = o_stream.EndRead(p_result);
            if (x_bytes_read > 0) {
                Console.Write(Encoding.Default.GetString(
                    o_byte_arr,
                    0,
                    x_bytes_read));

                o_stream.BeginRead(o_byte_arr, 0,
                    o_byte_arr.Length, o_callback,
                    null);
            } else {
                o_stream.Close();
            }
        }
        public static void Main() {
            new MagnaCartaDemo() ;
        }
    }
}

In the class constructor, we define a byte array that will be used to read data from the stream; since we are dealing with FileStream, we are limited to byte, rather than character, operations. The next step is to create the stream that will be used to read from the file. Notice that we use the constructor form, which explicitly enables asynchronous operations.

Warning

It’s important to specify that the FileStream should allow asynchronous operations in the constructor. If the default method of invoking the constructor is used, the BeginRead and BeginWrite methods will still function but will be working synchronously.

We then create the callback delegate and call the BeginRead method to start the asynchronous read. The BeginRead method accepts a byte array, offsets and lengths to store data in the array, the delegate that should be called when an operation is complete, and an object that the programmer can use to differentiate between I/O operations. Since we are working with only one file in this example, we have set the reference object to null. The remaining statement in the class constructor prints out a message indicating that the current thread isn’t blocking on the I/O operations.

After calling the BeginRead method, the current thread is free to do other things. When a read operation is complete, the method specified by delegate (readFinished in the example) will be called. In this method, we need to call EndRead to find out how many bytes have been read. If the number of bytes is 0, we have reached the end of the stream, and we close the FileStream instance. If there are bytes to be processed, we translate them into a string and write the result to the console. In order to continue reading from the file, we need to invoke BeginRead after each delegate callback until no bytes remain. Because the BeginRead method needs to be called repeatedly, it’s important to keep a local reference to the delegate and the data array. Creating an instance of this class leads to the following output:

>>>> Current thread is not blocking!
Magna Carta

A translation of Magna Carta as confirmed by Edward I with his seal in
 1297

[Preamble] EDWARD by the grace of God, King of England, Lord of Irelan
d, and Duke of Guyan, to all Archbishops, Bishops, etc. We have seen t
he Great Charter of the Lord HENRY, sometimes King of England, our fat
her, of the Liberties of England, in these words: Henry by the grace o
f God, King of England, Lord of Ireland, Duke of...

We have trimmed the output for brevity. The important part to note is that the message appears before the document text. If the stream had not been opened for asynchronous operations, this message would have appeared at the end of the output, indicating that the thread is, contrary to the message, blocking after all.

Asynchronous I/O Summary

The .NET model for asynchronous operations is simple to use and can provide significant performance improvements over synchronous I/O. The callback delegate is used in various places in the .NET Framework, including the networking classes, and once you’ve mastered asynchronous I/O, it’s a simple matter to apply the knowledge to perform asynchronous network operations. The limitation of asynchronous I/O is that the character-based support of the reader and writer classes is not available.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset