Last Updated:

Using Delegates in Asynchronous Programming

Every .NET object developer who wants to provide an asynchronous interface must follow the pattern just described. However, most developers do not need to develop their own solution for an asynchronous interface to their objects. Delegates provide a very simple way to support asynchronous operations for any method without modifying the developed classes. Of course, all of this needs attention, because when writing an object, some assumptions may have been made about the thread in which it runs, as well as about its synchronization requirements.

The two Asynch examples use the Customers object from the Customer assembly we examined. The first example, AsynchWithoutCallback, asynchronously registers new clients and performs processing while waiting for each registration to complete. The second example, AsynchWithCallback (Asynch with Callback), uses a callback function to process data asynchronously. In addition to the fact that the program can process data while waiting for registration to complete, the callback allows the system to perform some asynchronous action for each individual registration.

In the examples, we only output the information to the console to show where the work could have been done. To increase the latency to simulate long-term data processing, we placed the Thread::Sleep () calls in Customer::RegisterCustomer and in the sample program. Now let's look at the code in the examples.

Suppose a user wants to call the RegisterCustomer method asynchronously. The calling program simply declares a delegate with the same signature as the method.

public _delegate int RegisterCustomerCbk(
String *FirstName, // String
String *LastName, // String
String *EmailAddress); // Line

This turns the actual method into a callback function:

RegisterCustomerCbk *rcc = new
RegisterCustomerCbk{
customers, // customers
Customers::RegisterCustomer); // Clients

Invoke Start and End

 

When you declare a delegate, the compiler generates a class with three methods: BeginlnvokeEndlnvoke, and InvokeBeginlnvoke and Endlnvoke are typical safety methods.

They correspond to the BeginXXX and EndXXX methods and allow the delegate to be invoked asynchronously. When calling a delegate, the compiler uses the Invoke method. To call RegisterCustomer asynchronously, you only need to use the Beginlnvoke and Endlnvoke methods.

RegisterCustomerCbk *rcc = new RegisterCustomerCbk(
customers, // customers
Customers::RegisterCustomer); // Clients for(int i = 1; i < 5; i++) {
firstName = String::Concat(// String
"FirstName", i.ToString());
lastName = String::Concat(// String
"SecondName", (i * 2).ToString());
emailAddress = String::Concat (// String
i.ToString(), ".biz"); lAsyncResult *ar =
rcc › Begin!nvoke(firstName, lastName, emailAddress, 0, 0);
while(!ar › IsCompleted)
{
Console::WriteLine(
"Could do some work here while waiting for customer
registration to complete.");
// "I can do some work here
//while waiting
// complete client registration.");
ar › AsyncWaitHandle › WaitOne(1, false);
}
customerld = rcc › EndInvoke(ar);
Console::WriteLine(
Added Customerld: {0}",
// "Added Customerld: {0}"
customerId.ToString());
}

The program periodically waits for AsyncWaitHandle to see if the registration has ended or not. If not, then in the meantime the program could do some work. If Endlnvoke is called before RegisterCustomer completes, execution is blocked until RegisterCustomer completes.

Asynchronous callback

 

Instead of waiting on the handle, you can pass a callback function to the Beginlnvoke (or BeginXXX) method. This is exactly what the AsynchWithCallback (Asynch callback) sample does.

RegisterCustomerCbk *rcc = new RegisterCustomerCbk(customers, // customers
Customers::RegisterCustomer);
// AsyncCallback clients *cb =
new AsyncCallback(this, CustomerCallback);
Object *objectState; // An object
JAsyncResult *ar; forUnt i = 5; i < 10; i++) {
firstName = String::Concat(// String
"FirstName", i.ToString());
lastName = String::Concat(// String
"SecondName", (i * 2).ToString());
emailAddress =
String::Concat(i.ToString(), ".biz"); // Line
objectState = _box(i);
ar = rcc › Begin!nvoke(firstName,
lastname,
emailAddress,
cb,
objectState);
}
Console::WriteLine(
"Finished registrations…
could do some work here.");
// "End registration... can do
// some work here."
Thread::Sleep(25); // Thread:: Idle
Console::WriteLine(
"Finished work..waiting to let registrations complete.");
// "Finished. Waiting for the end of registration.");
Thread::Sleep(1000); // Thread:: Idle

Then we get the result in the callback function:

void CustomerCallback(lAsyncResult *ar)
{
int customerld;
*asyncResult =
dynamic_cast<AsyncResult *>(ar);
RegisterCustomerCbk *rcc =
dynamic_cast<RegisterCustoraerCbk *> (asyncResult › AsyncDelegate);
customerld = rcc › EndInvoke(ar);
Console::WriteLine(
AsyncState: {0} Customerld {1} added.",
ar › AsyncState, customerld.ToString());
Console::WriteLine(
"Could do processing here.");
// "Can do processing here."
return;
}

In this option, you can perform some steps before each tenant is enrolled.