Up
Coroutines » Function lists and VB coroutines

Function lists and VB coroutines

Last modified on August 15, 2012 17:21

This topic describes how to batch multiple asynchronous tasks with coroutines using function lists. You will likely use this technique when writing coroutines in Visual Basic.NET

It's also useful in C# when you want to build up a list of things to do asynchronously rather than define all those tasks within a single iterator method.



Coroutines in VB.NET

The iterator approach to batching asynchronous tasks is a popular choice in C#.

That choice is not available to Visual Basic.NET programmers because iterators aren't implemented in VB.NET. 

Fortunately, you can still use the DevForce Coroutine class in VB to manage a sequence of asynchronous tasks. Instead of writing an iterator to produce the sequence, you construct a list of functions that return INotifyCompleted and pass the list to the Coroutine.

We call this the "function list approach" to coroutines.

The function list approach is also useful in C# when you want to build up a list of things to do asynchronously rather than define all those tasks within a single iterator method.

Serial coroutine with function list

The basic recipe is as follows

  1. Write a "batcher" function that builds a list of asynchronous functions, each returning an INotifyCompleted object.
  2. Call the Coroutine.Start method, passing in the list returned by the batcher.
  3. Either include a callback method or attach a handler to the Coroutine.Completed event.

Here is an example that loads all customers, updates them, and saves them back to the database.

Write the "batcher"

LoadAllCustomersUpdateAndSaveCore is the "batcher" in our example.

C#
private IEnumerable<Func<INotifyCompleted>> LoadAllCustomersUpdateAndSaveCore(NorthwindEntities manager)
{
   // List of asynchronous functions for the Coroutine to execute serially.
   var funcList = new List<Func<INotifyCompleted>>();

   // Get all customers
   funcList.Add(() => manager.Customers.ExecuteAsync());

   // Make a change to them and save them
   funcList.Add(() =>
    {
        UpdateAllCachedCustomers(manager); // sync operation to update customers in cache - for example
       return manager.SaveChangesAsync(); // asynchronous operation returning INotifyCompleted
   });

   // return the list
   return funcList;
}
VB
Private Function LoadAllCustomersUpdateAndSaveCore(ByVal manager As NorthwindEntities) _
       As IEnumerable(Of Func(Of INotifyCompleted))

 ' List of asynchronous functions for the Coroutine to execute serially.
   Dim funcList = New List(Of Func(Of INotifyCompleted))

   ' Get all customers
   Dim loadFnc As Func(Of INotifyCompleted) =
        Function()
           Return manager.Customers.ExecuteAsync() ' return an INotifyCompleted
       End Function

    funcList.Add(loadFnc)

   ' Make a change to them and save them
   Dim saveFnc As Func(Of INotifyCompleted) =
        Function()
            UpdateAllCachedCustomers(manager)  ' sync operation to update customers in cache - for example
           Return manager.SaveChangesAsync()  ' return an INotifyCompleted
       End Function

    funcList.Add(saveFnc)  

   ' return the list
     Return funcList   
   End Function

Observations:

  • One batcher function fully defines the list of asynchronous tasks to process. Alternatively, you could build up that list externally and then submit it to the Coroutine class. This example assumes you want to think about the batch as a single, unified process.

  • Nesting the asynchronous function definitions within the batcher makes for easier reading as all steps of the process are in one place.

  • Each asynchronous function returns a single implementer of INotifyCompleted.

  • Within that function, all code except the final line must be synchronous. Do not put two asynchronous methods in the same function.  Including synchronous functionality is optional.

  • The last line of the batcher returns the function list it's built up.

Call Coroutine.Start

The Coroutine.Start method of the DevForce Coroutine class will accept either an iterator or a list of asynchronous functions.  The Coroutine works the same in either case, pausing until each asynchronous function completes.  

C#
var op = Coroutine.Start(LoadAllCustomersUpdateAndSaveCore(manager));
op.Completed += (s, e) => {
       if (e.CompletedSuccessfully) {
           ShowMessage("All customers were updated");
        } else {
           ShowMessage(e.Error.Message);
        }
      };
VB
Dim op = Coroutine.Start(LoadAllCustomersUpdateAndSaveCore(manager))
AddHandler op.Completed, Sub(s As Object, e As CoroutineCompletedEventArgs)
       If e.CompletedSuccessfully Then
            ShowMessage("All customers were updated")
       Else
            ShowMessage(e.Error.Message)
       End If
     End Sub

Parallel coroutine with function list

We can adopt a similar approach to run a collection of asynchronous tasks in parallel

Imagine a batcher called LoadReferenceEntitiesCore that asynchronously loaded sets of reference entities such as colors, states, unit types, etc. You would write this in precisely the same way that you did LoadAllCustomersUpdateAndSaveCore, the only difference would be in the functions that you added to the funcList.

We'd call StartParallel instead of Start as in this example, which also uses a callback instead of an event handler.

C#
Coroutine.StartParallel(
    LoadAllCustomersUpdateAndSaveCore(manager),        // batcher
   (op) =>                                            // callback
   {
     if (op.CompletedSuccessfully) {
        ShowMessage("References entities loaded");
      } else {
        ShowMessage(op.Error.Message);
      }
    }
);
VB
Coroutine.StartParallel( _
      LoadReferenceEntitiesCore(manager), _             ' batcher
     Sub(op As CoroutineOperation)                     ' callback
            If op.CompletedSuccessfully Then
                 ShowMessage("Reference entities loaded")
            Else
                 ShowMessage(op.Error.Message)
            End If
     End Sub
)
Tags:
Created by DevForce on March 08, 2011 13:32

This wiki is licensed under a Creative Commons 2.0 license. XWiki Enterprise 3.2 - Documentation. Copyright © 2015 IdeaBlade