Architecture Net

Использование делегатов в асинхронном программировании


Каждый разработчик объектов .NET, желающий предоставить асинхронный интерфейс, должен следовать только что описанному шаблону. Однако большинству разработчиков не нужно разрабатывать собственное решение для асинхронного интерфейса к своим объектам. Делегаты обеспечивают очень простой способ поддержки асинхронных операций для любого метода без какого-либо изменения разработанных классов. Конечно, все это требует внимания, потому что при написании объекта, возможно, были сделаны некоторые предположения относительно потока, в котором он выполняется, а так-же относительно его требований к синхронизации.


Два примера Asynch используют объект Customers (Клиенты) из изученной нами сборки Customer (Клиент). Первый пример, AsynchWithoutCallback, асинхронно регистрирует новых клиентов и выполняет обработку во время ожидания завершения каждой регистрации. Второй пример, AsynchWithCallback (Asynch с обратным вызовом), использует функцию обратного вызова для асинхронной обработки данных. В дополнение к тому, что программа может обрабатывать данные во время ожидания завершения регистрации, обратный вызов позволяет системе произвести некоторое асинхронное действие для каждой отдельной регистрации.

В примерах мы только выводим информацию на консоль, чтобы показать, где могла бы быть сделана работа. Для увеличения времени ожидания, чтобы смоделировать продолжительную обработку данных, мы поместили вызовы Thread: : Sleep () (Поток::


Режим ожидания) в Customer: : RegisterCustomer и в программу-пример. Теперь рассмотрим код в примерах.

Предположим, что пользователь хочет вызвать метод RegisterCustomer асинхронно. Вызывающая программа просто объявляет делегат с той же самой сигнатурой, что и у метода.

public _delegate int RegisterCustomerCbk(
String *FirstName, // Строка
String *LastName, // Строка
String *EmailAddress); // Строка

Таким образом реальный метод превращается в функцию обратного вызова:

RegisterCustomerCbk *rcc = new
RegisterCustomerCbk{
customers, // клиенты
Customers::RegisterCustomer); // Клиенты


Вызов (invoke) начала (Begin) и конца (End)

При объявлении делегата компилятор генерирует класс с тремя методами: Beginlnvoke, Endlnvoke и Invoke (Вызвать). Beginlnvoke и Endlnvoke — методы с типовой безопасностью. Они соответствуют методам BeginXXX и EndXXX и позволяют вызывать делегат асинхронно. При вызове делегата компилятор использует метод Invoke (Вызвать)27. Чтобы асинхронно вызвать RegisterCustomer, достаточно использовать только методы Beginlnvoke и Endlnvoke.

RegisterCustomerCbk *rcc = new RegisterCustomerCbk(
customers, // клиенты
Customers::RegisterCustomer); // Клиенты for(int i = 1; i < 5; i++) {
firstName = String::Concat( // Строка
"FirstName", i.ToString ());
lastName = String::Concat( // Строка
"SecondName", (i * 2).ToString{));
emailAddress = String::Concat ( // Строка
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.");
// "Могу сделать некоторую работу здесь
//во время ожидания
// завершения регистрации клиента.");
ar->AsyncWaitHandle->WaitOne(1, false);
}
customerld = rcc->End!nvoke(ar) ;
Console::WriteLine(
Added Customerld: {0}",
// " Добавлен Customerld: {0}"
customerId.ToString());
}

Программа периодически ждет AsyncWaitHandle, чтобы увидеть, закончилась или нет регистрация. Если нет, то тем временем программа могла бы выполнить некоторую работу. Если Endlnvoke вызывается до завершения RegisterCustomer, то выполнение блокируется до завершения RegisterCustomer.

Асинхронный обратный вызов

Вместо ожидания на дескрипторе, можно передать функцию обратного вызова в Beginlnvoke (или BeginXXX) метод. Именно так и делается в примере AsynchWithCallback (Asynch с обратным вызовом).

RegisterCustomerCbk *rcc = new RegisterCustomerCbk( customers, // клиенты
Customers::RegisterCustomer);
// Клиенты AsyncCallback *cb =
new AsyncCallback(this, CustomerCallback);
Object *objectState; // Объект
JAsyncResult *ar; forUnt i = 5; i < 10; i++) {
firstName = String::Concat( // Строка
"FirstName", i.ToString());
lastName = String::Concat( // Строка
"SecondName", (i * 2).ToString());
emailAddress =
String::Concat(i.ToString(), ".biz"); // Строка
objectState = _box(i);
ar = rcc->Begin!nvoke( firstName,
lastName,
emailAddress,
cb,
objectState);
}
Console::WriteLine(
"Finished registrations...
could do some work here.");
// "Закончена регистрация... могу сделать
// некоторую работу здесь."
Thread::Sleep(25); // Поток:: Бездействие
Console::WriteLine(
"Finished work..waiting to let registrations complete.");

// "Закончена работа., жду конца регистрации.");
Thread::Sleep(1000); // Поток:: Бездействие

Потом мы получаем результат в функции обратного вызова:

void CustomerCallback(lAsyncResult *ar)
{
int customerld;
AsyncResult *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.");
// " Могу сделать обработку здесь."
return;
}

В этом варианте можно выполнить некоторые действия до завершения регистрации каждого клиента.

CompEbook.ru Железо, дизайн, обучение и другие


Содержание раздела