Немного про асинхронные вызовы
При разборе кода атрибута синхронизации нам придется часто упоминать такое понятие как асинхронный вызов. Уместно сделать отступление и разобрать код, демонстрирующий работу с асинхронными вызовами.
Рассмотрим следующую ситуацию. Сервер предоставляет услуги по проведению сложных математических вычислений, требующих значительных временных затрат.
При использовании синхронных вызовов клиент должен был бы последовательно вызывать необходимые методы. При этом при каждом синхронном вызове клиент блокируется до получения ответа, что не дает ему возможность вызывать методы сервера параллельно и выполнять какую-либо другую полезную работу во время ожидания результатов от сервера.
При использовании асинхронных вызовов клиент может вызывать методы сервера параллельно и в процессе ожидания выполнять дополнительную работу.
В данном случае услуги, предоставляемые сервером, сводятся к выполнению двух арифметических операций (сложение и умножение на 2). Работа, которую клиент выполняет в ожидании ответов от сервера, состоит в выводе на консоль отметок о завершении очередного 100 mс временного интервала.
using System;
using System.Threading;
using System.Runtime.Remoting;
public class Server {
public static bool Sum(int x, int y, out int z) {
Console.WriteLine(
"Server (Sum method) thread = " +
Thread.CurrentThread.GetHashCode()+
"; PoolThread = "+
Thread.CurrentThread.IsThreadPoolThread);
Thread.Sleep(1000);
z = 0;
try {
z = checked((int)(x + y));
}
catch (Exception) {
return false;
}
return true;
}
public static bool MultBy2(int x, out int y) {
Console.WriteLine {
"Server (MultBy2 method) thread = " +
Thread.CurrentThread.GetHasheode()+
"; PoolThread = "+
Thread.CurrentThread.IsThreadPoolThread);
Thread.Sleep(1000);
y = 0;
try {
у = checked((int) (x*2));
}
catch (Exception) {
return false;
}
return true;
}
}
Public class Client {
private static int workCount = 0;
private delegate bool HardFunction2Args (
int x, int y, out int result);
private delegate bool HardFunctionlArg (
int x, out int result);
private static void SumCallback(IAsyncResult ar) {
int z;
HardFunction2Args sum =
(HardFunction2Args)ar.AsyncState;
bool result = sum.Endlnvoke(out z, ar);
if (result) Console.WriteLine (
"SumCallback: Sum = " + z);
else Console.WriteLine (
"SumCallback: Bad arguments for Server.Sum
workCount++;
}
private static void MultCallback(IAsyncResult ar) {
int z;
HardFunctionlArg mult =
(HardFunctionlArg)ar.AsyncState;
bool result = mult.Endlnvoke(out z, ar);
if (result) Console.WriteLine (
"MultCallback: MultBy2 = " + z);
else Console.WriteLine (
"MultCallback: Bad argument for MultBy2");
workCount++;
}
public static void Main() {
int sumResult, multResult, count = 0;
Console.WriteLine("Client thread = " +
Thread.CurrentThread.GetHashCode() + PoolThread = "+
Thread.CurrentThread.IsThreadPoolThread);
HardFunction2Args sum =
new HardFunction2Args(Server.Sum);
HardFunctionlArg mult =
new HardFunctionlArg(Server.MultBy2);
AsyncCallback sumCallback =
new AsyncCallback(SumCallback);
AsyncCallback multCallback =
new AsyncCallback(MultCallback);
IAsyncResult arSum = sum.Beginlnvoke(3, 4,
out sumResult, sumCallback, sum);
IAsyncResult arMult = mult.Beginlnvoke(5,
out multResult, multCallback, mult);
while (workCount < 2) {
Console.WriteLine("Client thread: count = "+ count++);
Thread.Sleep(100);
}
Console.WriteLine("Bye!");
}
}
Комментарии к коду.
Сервер и клиент представлены соответственно классами Server и Client.
Сервер
Сервер реализует два статических метода:
• Метод public static bool Sum(int x, int у, out int z) {… } обеспечивает сложение двух чисел типа int. Результат записывается в переменную z типа int. В случае возникновения переполнения возвращается false, при его отсутствии — true.
Временная сложность проводимых вычислений имитируется с помощью вызова Thread.Sleер(1000).
В самом начале, еще до проведения вычислений, на консоль выводится хеш потока, выполняющего вызов (Thread.CurrentThread.GetHashCode()), и информация о принадлежности данного потока классу рабочих потоков из пула потоков (Thread.CurrentThread.IsThreadPoolThread)).
• Метод public static bool MuitBy2(int x, out int y) {… } обеспечивает умножение числа на 2 и реализован аналогично предыдущему методу.
Здесь важно отметить, что разработчик сервера не заботится о том, как именно будут вызываться методы сервера клиентами — синхронно или асинхронно. Все зависит от клиента. Он может вызывать методы сервера как синхронно, так и асинхронно.
Клиент. Типы используемых делегатов
В данном примере клиент вызывает методы сервера асинхронно, что достигается за счет использования делегатов.
В коде клиента используются делегаты трех типов:
• HardFunction2Args
Этот тип определяется в классе client: