Асинхронные делегаты
Делегат можно вызвать на выполнение либо синхронно, как во всех приведенных ранее примерах, либо асинхронно с помощью методов BeginInvoke и EndInvoke. При вызове делегата с помощью метода BeginInvoke среда выполнения создает для исполнения метода отдельный поток и возвращает управление оператору» следующему за вызовом. При этом в исходном потоке можно продолжать вычисления.
Если при вызове BeginInvoke был указан метод обратного вызова, этот метод вызывается после завершения потока. Метод обратного вызова также задается с помошью делегата, при этом используется стандартный делегат AsyncCallback. В методе обратного вызова для получения возвращаемого значения и выходных параметров применяется метод EndInvoke.
Если метод обратного вызова не был указан в параметрах метода BeginInvoke, метод EndInvoke можно использовать в потоке, инициировавшем запрос.
В листинге 10.11 приводятся два примера асинхронного вызова метода, выполняющего разложение числа на множители. Листинг приводится по документации Visual Studio с некоторыми изменениями.
Класс Factorizer содержит метод Factorize, выполняющий разложение на множители. Этот метод асинхронно вызывается двумя способами: в методе Num1 метод обратного вызова задается в BeginInvoke, в методе Num2 имеют место ожидание завершения потока и непосредственный вызов EndInvoke.
Листинг 10.11. Асинхронные делегаты
using System;
using System.Threading;
using System.Runtime.Remoting.Messaging;
// асинхронный делегат public
delegate bool AsyncDelegate(int Num, out int m1, out int m2);
// класс, выполняющий разложение числа на множители
public class Factorizer
{
public bool Factorize(int Num, out int m1, out int m2)
{
ml = 1; m2 = Num;
for (int i = 2; i < Num; i++)
if (0 == (Num % i)) { ml = i; m2 = Num / i; break; }
if (1 == ml) return false; else return true;
}
}
// класс, получающий делегата и результаты
public class PNum
{
private int Number;
public PNum(int number)
{ Number = number; }
[OneWayAttribute()]
// метод, получающий результаты
public void Res(IAsyncResult ar)
{
int ml, m2;
// получение делегата из AsyncResult
AsyncDelegate ad = (AsyncDelegate)((AsyncResult)ar).AsyncDelegate;
// получение результатов выполнения метода Factorize a
d.EndInvoke(out m1, out m2, ar);
// вывод результатов
Console.WriteLine("Первый способ : множители {0} : {1} {2}",
Number, ml, m2);
}
// демонстрационный класс
public class Simple
{
// способ 1: используется функция обратного вызова
public void Num1()
{
Factorizer f = new Factonzer();
AsyncDelegate ad = new AsyncDelegate(f.Factorize);
int Num = 1000589023, tmp;
// создание экземпляра класса, который будет вызван
// после завершения работы метода Factorize
PNum n = new PNum(Num);
// задание делегата метода обратного вызова
AsyncCallback callback = new AsyncCallback(n.Res);
// асинхронный вызов метода Factorize
IAsyncResult ar = ad.BeginInvoke(Num, out tmp, out tmp,
callback, null);
}
//
// здесь - выполнение неких дальнейших действий
// ...
// способ 2: используется ожидание окончания выполнения
public void Num2()
{
Factorizer f = new Factorizer();
AsyncDelegate ad = new AsyncDelegate(f.Factorize);
int Num = 1000589023, tmp;
// создание экземпляра класса, который будет вызван
// после завершения работы метода Factorize
PNum n = new PNum(Num);
// задание делегата метода обратного вызова
AsyncCallback callback = new AsyncCallback(n.Res);
// асинхронный вызов метода Factorize
IAsyncResult ar = ad.BeginInvoke(Num, out tmp, out tmp,
null, null);
// ожидание завершения
ar.AsyncWaitHandle.WaitOne(10000, false);
if (ar.IsCompleted)
{
int m1, m2;
// получение результатов выполнения метода Factorize
ad.EndInvoke(out ml, out m2, ar);
// вывод результатов
Console.WriteLine("Второй способ : множители {0} : {1} {2}", Num, ml, m2);
}
}
public static void Main()
{
Simple s = new Simple();
s.Num1();
s.Num2();
}
}
}
Результат работы программы:
Первый способ : множители 1000589023 : 7 142941289
Второй способ : множители 1000589023 : 7 142941289