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

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

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

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

Введение

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

Делегат – это специальный метод, который инкапсулирует метод и представляет сигнатуру метода. Из главного потока, когда метод вызывается с помощью делегата, главный поток ждет пока метод, который был вызван, завершится. Это может вам дорого обойтись, если ваш код выполняет медленную операцию, например, загрузку большого файла с вашего сервера.

Давайте рассмотрим пример, когда метод вызывается асинхронно используя делегат.

1 delegate int SomeDelegate(int x);
2  
3 class Program
4 {
5     static void Main(string[] args) {
6         SomeDelegate sd = SquareNumber; // Создаем делегат
7         Console.WriteLine("Перед вызовом SquareNumber");
8         int res = sd(10);
9         Console.WriteLine("Возвращаемcя в главный метод");
10         Console.WriteLine(res);
11         Console.ReadLine();
12     }
13  
14     static int SquareNumber(int a) {
15         Console.WriteLine("SquareNumber вызван. Обработка..");
16         Thread.Sleep(2000);
17         return a * a;
18     }
19 }

Как вы видите, главная функция (метод Main) ждет пока метод SquareMethod завершится. Это происходит потому, что SquareMethod был вызван в том же потоке, что и метод Main.

Однако, приложив немного усилий, вы можете использовать делегаты для вызова любого метода асинхронно (это работает когда делегат имеет только один метод в своем списке вызовов). Так работают методы BeginInvoke и EndInvoke класса Delegate.

BeginInvoke, EndInvoke и IAsyncResult

BeginInvoke инициирует асинхронный вызов в отдельном потоке из ThreadPool (пул потоков). Последний принимает нескольких параметров – необходимый метода (который вы хотите вызвать асинхронно) и два дополнительных необязательных параметра называемых параметром обратного вызова и параметром состояния. Преимущество в использовании BeginInvoke заключается в том, что он немедленно возвращает управление и не ждет окончания асинхронного вызова.

BeginInvoke возвращает ссылку на объект реализации интерфейса IAsyncResult, который может быть использован для мониторинга прогресса асинхронного вызова.

Метода EndInvoke извлекает результаты асинхронного вызова и освобождает ресурсы использованные потоком. EndInvoke принимает out и ref параметры метода, который вы хотите выполнить асинхронно, плюс ссылку на IAsyncResult, которую вернул BeginInvoke.

Если вы не поняли как работать с методами BeginInvoke() и EndInvoke(), не переживайте, сейчас мы разберем короткие примеры с их участием.

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

Теперь, когда вы знаете как работают методы BeginInvoke и EndInvoke класса Delegate, давайте рассмотрим различные способы и шаблоны доступные при использовании BeginInvoke() и EndInvoke() для создания асинхронных вызовов.
Шаблон EndInvoke – в этом шаблоне (паттерне) мы используем метод BeginInvoke из главной функции Main для вызова метода, который что-то сделает в главном потоке и затем вызовет EndInvoke(). Это удобно, когда вы хотите продолжить работу потока во время выполнения асинхронного вызова. При этом EndInvoke() ничего не возвращает пока не будет завершен асинхронный вызов. Другими словами, главный поток ждет пока завершится операция асинхронного вызова.

WaitHandle – используя эту технику, главный метод вызывает асинхронно метод и ждет WaitHandle перед вызовом EndInvoke().

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

Шаблон Callback – в этом шаблоне главный поток инициирует асинхронный вызов, но не ждет и не проверяет завершение вызываемого потока. Этот шаблон может быть использован, если вы не хотите обрабатывать результаты асинхронного вызова в главном потоке.

Давайте рассмотрим примеры асинхронного вызова используя шаблон EndInvoke.

1 delegate int SomeDelegate(int x);
2  
3 class Program
4 {
5     static void Main(string[] args) {
6         SomeDelegate sd = SquareNumber; // Создаем делегат
7         Console.WriteLine("Перед вызовом SquareNumber");
8         IAsyncResult asyncRes = sd.BeginInvoke(10, null, null);
9         Console.WriteLine("Возвращаемcя в главный метод");
10         int res = sd.EndInvoke(asyncRes);
11         Console.WriteLine(res);
12         Console.ReadLine();
13     }
14  
15     static int SquareNumber(int a) {
16         Console.WriteLine("SquareNumber вызван. Обработка..");
17         Thread.Sleep(2000);
18         return a * a;
19     }
20 }

Как вы видите, BeginInvoke() используется для асинхронного вызова, который немедленно возвращает управление после своего вызова, и таким образом, метод Main может продолжить обработку, пока выполняется асинхронная операция. BeginInvoke() возвращает объект IAsyncResult в вызывающий поток.

IAsyncResult asyncRes = sd.BeginInvoke(10, null, null);

Затем мы используем EndInvoke(), чтобы получить результаты асинхронного вызова. EndInvoke() имеет параметр, который является ссылкой на IAsyncResult, возвращаемый BeginInvoke.

int res = sd.EndInvoke(asyncRes);

EndInvoke() использует этот параметр, чтобы найти поток, к которому это относится и который может быть использован для мониторинга хода выполнения асинхронного вызова.

Вот и все. Мы только что сделали асинхронный вызов метода с помощью делегата!

В следующей статье я покажу вам, как использовать шаблоны Polling и Callback для выполнения асинхронных вызовов.

Надеюсь, что вам понравилась статья, и я благодарю Вас за внимание.