The asynchronous programming model pattern

The asynchronous programming model (APM) is an asynchronous operation that uses the IAsyncResult interface as its design pattern. It's also called the IAsyncResult pattern. For this purpose, the framework has provided the method named BeginXx and EndXx, in which Xx is the operation name, for instance, BeginRead and EndRead provided by the FileStream class to read bytes from a file asynchronously.

The difference in the synchronous Read() method with BeginRead() and EndRead() can be recognized from the method's declaration, as follows:

public int Read( 
  byte[] array, 
  int offset, 
  int count 
) 
public IAsyncResult BeginRead( 
  byte[] array, 
  int offset, 
  int numBytes, 
  AsyncCallback userCallback, 
  object stateObject 
) 
public int EndRead( 
  IAsyncResult asyncResult 
) 

As we can see, in the synchronous Read() method, we need three parameters; they are array, offset, and numBytes. In the BeginRead() method, there are two more parameter additions; they are userCallback, the method that will be called when the asynchronous read operation is completed, and stateObject, an object provided by the user that distinguishes the asynchronous read request from other requests.

Using the synchronous Read() method

Now, let's take a look at the following code, which we can find in the APM.csproj project, in order to distinguish the asynchronous BeginRead() method from the synchronous Read() method in a clearer way:

public partial class Program 
{ 
  public static void ReadFile() 
  { 
    FileStream fs = 
      File.OpenRead( 
        @"......LoremIpsum.txt"); 
    byte[] buffer = new byte[fs.Length]; 
    int totalBytes = 
      fs.Read(buffer, 0, (int)fs.Length); 
    Console.WriteLine("Read {0} bytes.", totalBytes); 
    fs.Dispose(); 
  } 
} 

The preceding code will synchronously read the LoremIpsum.txt file (included in the APM.csproj project), which means that the reading process has to be completed before executing the next process. If we run the preceding ReadFile() method, we will get the following output on the console:

Using the synchronous Read() method

Using the BeginRead() and EndRead() methods

Now, let's compare the synchronous reading process using the Read() method with the asynchronous reading process using the BeginRead() and EndRead() methods from the following code:

public partial class Program 
{ 
  public static void ReadAsyncFile() 
  { 
    FileStream fs =  
      File.OpenRead( 
        @"......LoremIpsum.txt"); 
    byte[] buffer = new byte[fs.Length]; 
    IAsyncResult result = fs.BeginRead(buffer, 0, (int)fs.Length,
      OnReadComplete, fs); 
    //do other work while file is read 
    int i = 0; 
    do 
    { 
      Console.WriteLine("Timer Counter: {0}", ++i); 
    } 
    while (!result.IsCompleted); 
    fs.Dispose(); 
  } 
  private static void OnReadComplete(IAsyncResult result) 
  { 
    FileStream fStream = (FileStream)result.AsyncState;
    int totalBytes = fStream.EndRead(result);
    Console.WriteLine("Read {0} bytes.", totalBytes);fStream.Dispose(); 
  } 
} 

As we can see, we have two methods named ReadAsyncFile() and OnReadComplete(). The ReadAsyncFile() method will read the LoremIpsum.txt file asynchronously and then invoke the OnReadComplete() method just after finishing reading the file. We have additional code to make sure that the asynchronous operation runs properly using the following do-while looping code snippet:

//do other work while file is read 
int i = 0; 
do 
{ 
  Console.WriteLine("Timer Counter: {0}", ++i); 
} 
while (!result.IsCompleted); 

The preceding do-while loop will iterate until the asynchronous operation is completed, as indicated in the IsComplete property of IAsyncResult. The asynchronous operation is started when the BeginRead() method is invoked, as shown in the following code snippet:

IAsyncResult result = 
  fs.BeginRead( 
    buffer, 0, (int)fs.Length, OnReadComplete, fs); 

After that, it will continue with the next process while it reads the file. The OnReadComplete() method will be invoked when the reading process is finished, and since the implementation of the OnReadComplete() method set the IsFinish variable to true, it will stop our do-while looping.

The output we will get by running the ReadAsyncFile() method is as follows:

Using the BeginRead() and EndRead() methods

From the screenshot of the preceding output, we can see that the iteration of the do-while loop is successfully executed when the reading process is run as well. The reading process is finished in the 64th iteration of the do-while loop.

Adding LINQ to the BeginRead() method invocation

We can also use LINQ to define the OnReadComplete() method so that we can replace that method using the anonymous method, as follows:

public partial class Program 
{ 
  public static void ReadAsyncFileAnonymousMethod() 
  { 
    FileStream fs = 
      File.OpenRead( 
        @"......LoremIpsum.txt"); 
    byte[] buffer = new byte[fs.Length]; 
    IAsyncResult result = fs.BeginRead(buffer, 0, (int)fs.Length,
      asyncResult => { int totalBytes = fs.EndRead(asyncResult); 
    Console.WriteLine("Read {0} bytes.", totalBytes); 
      }, null); 
    //do other work while file is read 
    int i = 0; 
    do 
    { 
      Console.WriteLine("Timer Counter: {0}", ++i); 
    } 
    while (!result.IsCompleted); 
    fs.Dispose(); 
  } 
} 

As we can see, we replace the invocation of the BeginRead() method with the following code snippet:

IAsyncResult result = 
  fs.BeginRead( 
    buffer, 
    0, 
    (int)fs.Length, 
    asyncResult => 
    { 
      int totalBytes = 
        fs.EndRead(asyncResult); 
      Console.WriteLine("Read {0} bytes.", totalBytes); 
    }, 
  null); 

From the preceding code, we can see that we don't have the OnReadComplete() method anymore since it has been represented by the anonymous method. We remove the FileStream instance in the callback because the anonymous method in lambda will access it using the closure. And if we invoke the ReadAsyncFileAnonymousMethod() method, we will get the exact same output as the ReadAsyncFile() method except the iteration count, since it depends on the CPU speed.

Besides the IsCompleted property, which is used to fetch the value that indicates whether the asynchronous operation is complete, there are three more properties we can use when dealing with IAsyncResult; they are as follows:

  • AsyncState: This is used to retrieve an object defined by the user that qualifies or contains information about an asynchronous operation
  • AsyncWaitHandle: This is used to retrieve WaitHandle (an object from the operating system that waits for exclusive access to shared resources), which indicates the completeness of the asynchronous operation
  • CompletedSynchronously: This is used to retrieve a value that indicates whether the asynchronous operation completed synchronously

Unfortunately, there are several shortages when applying APM, such as the inability to do a cancelation. This means that we cannot cancel the asynchronous operator because from the invocation of BeginRead until the callback is triggered, there is no way to cancel the background process. If LoremIpsum.txt is a gigabyte file, we have to wait until the asynchronous operation is finished instead of canceling the operation.

Note

The APM pattern is no longer recommended in a new development due to its obsolete technology.

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

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