9.9 Read a File Asynchronously


Problem

You need to read data from a file without blocking the execution of your code. This technique is commonly used if the file is stored on a slow backing store (such as a networked drive in a wide area network).

Solution

Create a separate class that will read the file asynchronously. Start reading a block of data using the FileStream.BeginRead method, and supply a callback method. When the callback is triggered, retrieve the data by calling FileStream.EndRead , process it, and read the next block asynchronously with BeginRead .

Discussion

The FileStream includes basic support for asynchronous use through the BeginRead and EndRead methods. Using these methods , you can read a block of data on one of the threads provided by the .NET thread pool, without needing to directly use the threading classes in the System.Threading namespace.

When reading a file asynchronously, you choose the amount of data that you want to read at a time. Depending on the situation, you might want to read a very small amount of data at a time (for example, if you are copying it block by block to another file) or a relatively large amount of data (for example, if you need a certain amount of information before your processing logic can start). You specify the block size when calling BeginRead , and you pass a buffer where the data will be placed. Because the BeginRead and the EndRead methods need to be able to access many of the same pieces of information, such as the FileStream , the buffer, the block size, and so on, it's usually easiest to encapsulate your asynchronous file reading code in a single class.

For example, consider the AsyncProcessor class shown in the following code. It provides a public StartProcess method, which starts an asynchronous read. Every time the read operation finishes, the OnCompletedRead callback is triggered and the block of data is processed . If there is more data in the file, a new asynchronous read operation is started. The AsyncProcessor reads 2 kilobytes (2048 bytes) at a time.

 using System; using System.IO; using System.Threading; public class AsyncProcessor {     private Stream inputStream;        // The amount that will be read in one block (2 KB).     private int bufferSize = 2048;     public int BufferSize {         get {return bufferSize;}         set {bufferSize = value;}     }     // The buffer that will hold the retrieved data.     private byte[] buffer;       public AsyncProcessor(string  file name) {         buffer = new byte[bufferSize];         // Open the file, specifying true for asynchronous support.         inputStream = new FileStream( file name, FileMode.Open,           FileAccess.Read, FileShare.Read, bufferSize, true);     }     public void StartProcess() {         // Start the asynchronous read, which will fill the buffer.         inputStream.BeginRead(buffer, 0, buffer.Length,           new AsyncCallback(OnCompletedRead), null);     }     private void OnCompletedRead(IAsyncResult asyncResult) {         // One block has been read asynchronously.         // Retrieve the data.         int bytesRead = inputStream.EndRead(asyncResult);         // If no bytes are read, the stream is at the end of the file.         if (bytesRead > 0) {             // Pause to simulate processing this block of data.             Console.WriteLine("\t[ASYNC READER]: Read one block.");             Thread.Sleep(TimeSpan.FromMilliseconds(20));             // Begin to read the next block asynchronously.             inputStream.BeginRead(             buffer, 0, buffer.Length, new AsyncCallback(OnCompletedRead),               null);         }else {             // End the operation.             Console.WriteLine("\t[ASYNC READER]: Complete.");             inputStream.Close();         }     } } 

The following example shows a Console application that uses the AsyncProcessor to read a 2-megabyte file.

 public class AsynchronousIO {     public static void Main() {         // Create a 1 MB test file.         FileStream fs = new FileStream("test.txt", FileMode.Create);         fs.SetLength(1000000);         fs.Close();         // Start the asynchronous file processor on another thread.         AsyncProcessor asyncIO = new AsyncProcessor("test.txt");         asyncIO.StartProcess();         // At the same time, do some other work.         // In this example, we simply loop for 10 seconds.         DateTime startTime = DateTime.Now;         while (DateTime.Now.Subtract(startTime).TotalSeconds < 10) {             Console.WriteLine("[MAIN THREAD]: Doing some work.");             // Pause to simulate a time-consuming operation.             Thread.Sleep(TimeSpan.FromMilliseconds(100));         }         Console.WriteLine("[MAIN THREAD]: Complete.");         Console.ReadLine();         // Remove the test file.         File.Delete("test.txt");     } } 

This is an example of the output you'll see when you run this test:

 [MAIN THREAD]: Doing some work.         [ASYNC READER]: Read one block.         [ASYNC READER]: Read one block. [MAIN THREAD]: Doing some work.         [ASYNC READER]: Read one block.         [ASYNC READER]: Read one block.         [ASYNC READER]: Read one block.         [ASYNC READER]: Read one block. [MAIN THREAD]: Doing some work.         [ASYNC READER]: Read one block.         [ASYNC READER]: Read one block.         [ASYNC READER]: Read one block.         . . . 



C# Programmer[ap]s Cookbook
C# Programmer[ap]s Cookbook
ISBN: 735619301
EAN: N/A
Year: 2006
Pages: 266

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net