• Программинг

Нужны источники бесперебойного питания?

Источники бесперебойного питания от дизельстор

C#. Асинхронный вызов метода используя шаблоны Polling и Callback

В предыдущей статье, Асинхронный вызов метода используя делегат BeginInvoke и шаблон EndInvoke, мы рассмотрели несколько способов использования асинхронного вызова с помощью делегатов. Также, разобрали, что асинхронно вызвать метод можно четырьмя способами используя BeginInvoke() и EndInvoke() класса Delegate. Эти четыре способа используют шаблоны EndInvoke, WaitHandle, Polling и Callback.

В этой статье мы рассмотрим как можно асинхронно вызвать метод используя шаблон Polling и Callback. Прочтите предыдущую статью, чтобы понять, как работают методы BeginInvoke() и EndInvoke(). Эта статья предполагает, что вы уже ориентируетесь в данных понятиях.
Асинхронный вызов метода с помощью шаблона Polling

В этом шаблоне вызывающий поток периодически опрашивает другой поток (выполняет асинхронную операцию), используя объект IAsyncResult, и проверяет, не завершился ли поток. Если поток все еще работает, то проверка продолжается. Приложение не вызывает EndInvoke(), пока не будет известно, что операция завершилась. Этот шаблон может быть использован, когда необходимо, чтобы пользовательский интерфейс был доступен во время выполнения операции. Давайте посмотрим пример:

1 delegate int SomeDelegateOne(int x);
2  
3 class PollingPattern
4 {
5     static void Main(string[] args)
6     {
7         SomeDelegateOne sd = SquareNumber; // создаем делегат
8         Console.WriteLine("Before SquareNumber Method Invoke");
9         IAsyncResult asyncRes = sd.BeginInvoke(10, null, null); // вызваем
10         Console.WriteLine("Back to Main Method");
11  
12         // Проверяем IAsyncResult.IsCompleted
13         while (asyncRes.IsCompleted == false)
14         {
15             Console.WriteLine("Square Number still processing");
16             Thread.Sleep(1000);  // эмулируем работу метода
17         }
18  
19         Console.WriteLine("Square Number processing completed");
20         int res = sd.EndInvoke(asyncRes);
21         Console.WriteLine(res);
22         Console.ReadLine();
23     }
24  
25     static int SquareNumber(int a)
26     {
27         Console.WriteLine("SquareNumber Invoked. Processing..");
28         Thread.Sleep(2000);
29         return a * a;
30     }
31 }

В приведенном коде поток инициирует асинхронный вызов, а затем опрашивает свойство IAsyncResult.IsCompleted для получения информации о завершении выполнения операции. До этого момента продолжается выполнение потока. EndInvoke вызывается только тогда, когда свойств IsCompleted вернет true.

Асинхронный вызов метода, используя шаблон Callback

В этом шаблоне, поток инициирует асинхронный вызов, но в отличие от шаблона Polling, он не ждет и не проверяет на завершение поток, который был вызван. Вместо этого используется функция обратного вызова. Когда вызванный асинхронный метод завершает свою работу, – запускается функция обратного вызова для обработки результатов и вызывается EndInvoke. Такой вариант может быть использован, когда вам не нужно обрабатывать результат асинхронного вызова в главном потоке во время его работы. Давайте рассмотрим еще один пример:

1 delegate int SomeDelegateTwo(int x);
2  
3 class CallbackPattern
4 {
5     static void Main(string[] args)
6     {
7         SomeDelegateTwo sd = SquareNumber; // создаем делегат
8         Console.WriteLine("Before SquareNumber Method Invoke");
9         // выполняем асинхронный вызов
10         IAsyncResult asyncRes = sd.BeginInvoke(10, new AsyncCallback(CallbackMethod), null);
11         Console.WriteLine("Back to Main Method. Doing Extra Processing...");
12         Thread.Sleep(500);
13         Console.WriteLine("Main method processing completed");
14         Console.ReadLine();
15     }
16  
17     static int SquareNumber(int a)
18     {
19         Console.WriteLine("SquareNumber Invoked. Processing..");
20         Thread.Sleep(3000);
21         return a * a;
22     }
23  
24     // Функция обратного вызова
25     static void CallbackMethod(IAsyncResult asyncRes)
26     {
27         Console.WriteLine("Callback invoked");
28         AsyncResult ares = (AsyncResult)asyncRes;
29         SomeDelegateTwo delg = (SomeDelegateTwo)ares.AsyncDelegate;
30         int result = delg.EndInvoke(asyncRes);
31         Console.WriteLine(result);
32     }
33 }

Как вы видите, мы определили метод CallbackMethod, который будет вызван по завершению работы (при этом вызвать можно все, что угодно). Эта функция обратного вызова, не что иное, как делегат типа AsyncCallback. Мы добавили этот делегат в функцию BeginInvoke() и он будет вызван когда завершится асинхронный вызов.

Я надеюсь, что вы уже начинаете понимать разницу между использованием функции обратного вызова и другими способами, представленными здесь. Здесь начальный поток (функция Main) не ждет и не проверяет завершение асинхронного вызова. Вместо этого используется функция обратного вызова, которая выполнится по окончанию работы асинхронного вызова. Функция обратного вызова вызовет EndInvoke(), обработает результат вызова и выведет на консоль.