How to perform asynchronous file operations in C#

Take advantage of the async and await keywords in C# to implement asynchronous file operations

How to perform asynchronous file operations in C#

Asynchronous programming

You can take advantage of asynchrony to perform resource-intensive I/O operations sans the need to block the main or the executing thread of the application. Asynchrony when used properly can increase the responsiveness and scalability of your applications to a considerable extent. This article presents an overview on asynchronous file operations using C#.

Why is asynchrony needed?

Asynchronous programming enables you to execute tasks separate from the main application thread, then notifies the thread when its execution is over. Asynchrony helps you execute tasks sans the need of holding up the execution flow or responsiveness of your application. You can leverage asynchrony to improve the performance and responsiveness of your application as the calling thread can continue to perform other operations while the method that can been called asynchronously continues to execute.

The async and await keywords

If you were to use .Net Framework and its earlier versions, you could leverage the BeginRead() and the EndRead() methods to implement asynchronous operations when working with files. Although these methods are still available as part of the newer versions of the .Net Framework, you can use the two new keywords async and await that have been introduced as part of .Net Framework 4.5. Hence, you can take advantage of async and await to implement asynchronous file operations when working with .Net Framework 4.5 or later. You can learn more about async and await on MSDN.

In using asynchrony you can make the user interface threads more responsive and perform other activities while the asynchronous operation is in progress. 

Performing asynchronous file operations

You can use the FileStream class to perform asynchronous I/O operations. In most cases, this would ensure that the calling thread is not blocked while the asynchronous file operation is in progress. To use this option, you should turn on the asynchronous option (with the option useAsync: true) when creating an instance of the FileStream class. This is shown in the code snippet given below.

FileStream fileStream = new FileStream("C:\\IDG.txt",

                FileMode.Append, FileAccess.Write, FileShare.None,

                bufferSize: 4096, useAsync: true);

The following method writes a text passed to it as parameter to a file asynchronously. Note the usage of the await keyword when calling the WriteAsync() method and the async keyword that is used in the method signature to imply this method would have one or more await statements.

static async Task WriteToFileAsync(string filePath, string text)

        {

            if (string.IsNullOrEmpty(filePath))

                throw new ArgumentNullException("filePath");

            if (string.IsNullOrEmpty(text))

                throw new ArgumentNullException("text");

            byte[] buffer = Encoding.Unicode.GetBytes(text);

            Int32 offset = 0;

            Int32 sizeOfBuffer = 4096;

            FileStream fileStream = null;

            try

            {

                fileStream = new FileStream(filePath, FileMode.Append, FileAccess.Write,

                FileShare.None, bufferSize: sizeOfBuffer, useAsync: true);

                await fileStream.WriteAsync(buffer, offset, buffer.Length);

            }

            catch

            {

                //Write code here to handle exceptions.

            }

            finally

            {

                if(fileStream != null)

                fileStream.Dispose();

            }

        }

It should be noted that if you don't include the await keyword inside an asynchronous method, the entire method would execute synchronously. Note that the usage of async and await doesn't create any additional threads. The following code snippet illustrates how you can call the WriteToFileAsync() method from the Main() method to write a text asynchronously.

 static Task WriteToFile()

        {

            string filePath = "C:\\IDG.txt";

            string text = "Hello World\r\n";

            return WriteToFileAsync(filePath, text);

        }

        static void Main(string[] args)

        {

            WriteToFile().Wait();

            Console.Write("Press any key to exit... ");

            Console.ReadKey();           

        }

The following method reads text from a file asynchronously. You would need to pass the file name and the size of the buffer as parameters. Note how the object readBuffer is set to null in the catch block if an exception occurs so that the caller method can identify if an exception has occurred while attempting to read text from the file.

 static async Task<string> ReadFromFileAsync(string filePath, int bufferSize)

        {

            if (bufferSize < 1024)
                throw new ArgumentNullException("bufferSize");

            if (string.IsNullOrEmpty(filePath))

                throw new ArgumentNullException("filePath");

            StringBuilder readBuffer = null;

            byte[] buffer = new byte[bufferSize];

            FileStream fileStream = null;

            try

            {

                readBuffer = new StringBuilder();

                fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read,

                FileShare.Read, bufferSize: bufferSize, useAsync: true);

                Int32 bytesRead = 0;

                while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) > 0)

                {

                    readBuffer.Append(Encoding.Unicode.GetString(buffer, 0, bytesRead));

                }               

            }

            catch

            {

                readBuffer = null;

                //Write code here to handle exceptions;

            }

            finally

            {

                if (fileStream != null)

                    fileStream.Dispose();

            }

            return readBuffer.ToString();

        }

Note that we have used the FileStream class in the code examples presented in this post as StreamReader and StreamWriter doesn't provide support for asynchronous read and write operations. You can invoke the ReadFromFileAsync() method from the following method. Note that the ReadFromFile() method given below returns a Task<string> object.

 static async Task<string> ReadFromFile()

        {

            string filePath = "C:\\IDG.txt";

            return await ReadFromFileAsync(filePath, 4096);

        }

You can now call the ReadFromFile() method from the Main() method to retrieve and display all text from the file in the console window.

static void Main(string[] args)

        {

            string text = ReadFromFile().Result;

            Console.Write(text);      

            Console.ReadKey();            

        }

This article is published as part of the IDG Contributor Network. Want to Join?

To comment on this article and other InfoWorld content, visit InfoWorld's LinkedIn page, Facebook page and Twitter stream.
From CIO: 8 Free Online Courses to Grow Your Tech Skills
Notice to our Readers
We're now using social media to take your comments and feedback. Learn more about this here.