




















































In this article by Dirk Strauss, author of the book C# Programming Cookbook, he sheds some light on how to handle events, exceptions and tasks in asynchronous programming, making your application responsive.
(For more resources related to this topic, see here.)
Task-Based Asynchronous Pattern (TAP) is now the recommended method to create asynchronous code. It executes asynchronously on a thread from the thread pool and does not execute synchronously on the main thread of your application. It allows us to check the task's state by calling the Status property.
We will create a task to read a very large text file. This will be accomplished using an asynchronous Task.
public Task<int> ReadBigFile()
{
}
public Task<int> ReadBigFile()
{
var bigFile = File.OpenRead(@"C:temptaskFile.txt");
var bigFileBuffer = new byte[bigFile.Length];
var readBytes = bigFile.ReadAsync(bigFileBuffer, 0, " (int)bigFile.Length);
return readBytes;
}
Exceptions you can expect to handle from the ReadAsync() method are ArgumentNullException, ArgumentOutOfRangeException, ArgumentException, NotSupportedException, ObjectDisposedException and InvalidOperatorException.
public Task<int> ReadBigFile()
{
var bigFile = File.OpenRead(@"C:temptaskFile.txt");
var bigFileBuffer = new byte[bigFile.Length];
var readBytes = bigFile.ReadAsync(bigFileBuffer, 0, (int)bigFile.Length);
readBytes.ContinueWith(task =>
{
if (task.Status == TaskStatus.Running)
Console.WriteLine("Running");
else if (task.Status == TaskStatus.RanToCompletion)
Console.WriteLine("RanToCompletion");
else if (task.Status == TaskStatus.Faulted)
Console.WriteLine("Faulted");
bigFile.Dispose();
});
return readBytes;
}
namespace winformAsync
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
}
}
}
private async void button1_Click(object sender, EventArgs e)
{
}
private async void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("Start file read");
Chapter6.AsyncDemo oAsync = new Chapter6.AsyncDemo();
int readResult = await oAsync.ReadBigFile();
Console.WriteLine("Bytes read = " + readResult);
}
Take note though that the information displayed in your Output window will differ from the screenshot. This is because the file you used is different from mine.
The task is executed on a separate thread from the thread pool. This allows the application to remain responsive while the large file is being processed. Tasks can be used in multiple ways to improve your code. This recipe is but one example.
Exception handling in asynchronous programming has always been a challenge. This was especially true in the catch blocks. As of C# 6, you are now allowed to write asynchronous code inside the catch and finally block of your exception handlers.
The application will simulate the action of reading a logfile. Assume that a third-party system always makes a backup of the logfile before processing it in another application. While this processing is happening, the logfile is deleted and recreated. Our application, however, needs to read this logfile on a periodic basis. We, therefore, need to be prepared for the case where the file does not exist in the location we expect it in. Therefore, we will purposely omit the main logfile, so that we can force an error.
private async Task<int> ReadMainLog()
{
var bigFile = " File.OpenRead(@"C:tempLogMainLogtaskFile.txt");
var bigFileBuffer = new byte[bigFile.Length];
var readBytes = bigFile.ReadAsync(bigFileBuffer, 0, " (int)bigFile.Length);
await readBytes.ContinueWith(task =>
{
if (task.Status == TaskStatus.RanToCompletion)
Console.WriteLine("Main Log RanToCompletion");
else if (task.Status == TaskStatus.Faulted)
Console.WriteLine("Main Log Faulted");
bigFile.Dispose();
});
return await readBytes;
}
private async Task<int> ReadBackupLog()
{
var bigFile = " File.OpenRead(@"C:tempLogBackupLogtaskFile.txt");
var bigFileBuffer = new byte[bigFile.Length];
var readBytes = bigFile.ReadAsync(bigFileBuffer, 0, " (int)bigFile.Length);
await readBytes.ContinueWith(task =>
{
if (task.Status == TaskStatus.RanToCompletion)
Console.WriteLine("Backup Log " RanToCompletion");
else if (task.Status == TaskStatus.Faulted)
Console.WriteLine("Backup Log Faulted");
bigFile.Dispose();
});
return await readBytes;
}
In actual fact, we would probably only create a single method to read the logfiles, passing only the path as a parameter. In a production application, creating a class and overriding a method to read the different logfile locations would be a better approach. For the purposes of this recipe, however, we specifically wanted to create two separate methods so that the different calls to the asynchronous methods are clearly visible in the code.
public async Task<int> ReadLogFile()
{
int returnBytes = -1;
try
{
Task<int> intBytesRead = ReadMainLog();
returnBytes = await ReadMainLog();
}
catch (Exception ex)
{
try
{
returnBytes = await ReadBackupLog();
}
catch (Exception)
{
throw;
}
}
return returnBytes;
}
namespace winformAsync
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
}
}
}
private async void button1_Click(object sender, EventArgs e)
{
}
private async void button1_Click(object sender, EventArgs "e)
{
Console.WriteLine("Read backup file");
Chapter6.AsyncDemo oAsync = new Chapter6.AsyncDemo();
int readResult = await oAsync.ReadLogFile();
Console.WriteLine("Bytes read = " + readResult);
}
The fact that we can await in catch and finally blocks allows developers much more flexibility because asynchronous results can consistently be awaited throughout the application. As you can see from the code we wrote, as soon as the exception was thrown, we asynchronously read the file read method for the backup file.
In this article we looked at how TAP is now the recommended method to create asynchronous code. How tasks can be used in multiple ways to improve your code. This allows the application to remain responsive while the large file is being processed also how exception handling in asynchronous programming has always been a challenge and how to use catch and finally block to handle exceptions.
Further resources on this subject: