Асинхронный вызов метода

ОГЛАВЛЕНИЕ

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

Введение

В данной статье объясняются асинхронные вызовы методов и принципы их использования. После работы с делегатами, потоками и асинхронным вызовом настала пора поделиться знаниями по этому вопросу. Используются маленькие шаги и множество примеров. Вообще, будет показано, как вызывать методы асинхронно, как передавать параметры таким методам, и как узнать, когда метод завершает выполнение. Наконец, будет показан используемый шаблон команды для упрощения части кода. Большое преимущество асинхронного вызова метода .NET в том, что любой метод в проекте можно вызвать асинхронно без изменения кода метода. Важно знать множество хитростей, происходящих за кулисами внутри .NET, и они будут изучены в статье.

Синхронный против асинхронного

Следует объяснить синхронный и асинхронный вызовы метода на примере.

Синхронный вызов метода

Допустим, есть функция Foo(), требующая 10 секунд на выполнение.

private void Foo()
{
    // ждать 10 секунд.
    Thread.Sleep(10000);
}

Как правило, когда приложение вызывает функцию Foo(), ему приходится ждать 10 секунд, пока Foo() не завершится и управление не вернется вызывающему потоку. Предположим, надо вызвать Foo() 100 раз, тогда потребуется 1000 секунд для возврата управления вызывающему потоку. Такой тип вызова метода является синхронным.
1.    Вызывается Foo()
2.    Foo() выполняется
3.    Управление возвращается вызывающему потоку

Теперь Foo() вызывается с использованием делегатов, потому что большая часть работы, которая будет сделана тут, основана на делегатах. К счастью, в каркасе .NET уже есть делегат, позволяющий вызвать функцию, не принимающую параметров и не возвращающую значение. Делегат называется MethodeInvoker. Пора испытать его.

// создать делегат MethodInvoker, указывающий
// на функцию Foo().
MethodInvoker simpleDelegate = new MethodInvoker(Foo);

// вызов Foo
simpleDelegate.Invoke();

Даже в примере выше Foo() все еще вызывается синхронно. Вызывающему потоку придется ждать завершения функции Invoke(), пока управление не вернется к вызывающему потоку.

Асинхронный вызов метода

Но что если надо вызвать Foo() и не ждать завершения ее выполнения, если неважно, когда она завершится? Скажем, надо просто вызвать Foo 100 раз, не дожидаясь завершения ни одного из вызовов функции. По сути, сделать нечто под названием выстрелить и забыть. Вызывается функция, ее не ждут и забывают о ней. И не меняется ни строки кода в сверхсложной затейливой функции Foo().

// создать делегат MethodInvoker, указывающий на
// функцию Foo.
MethodInvoker simpleDelegate = new MethodInvoker(Foo);

// вызов Foo асинхронно
for(int i=0; i<100; i++)
    simpleDelegate.BeginInvoke(null, null);

Делается несколько замечаний о коде выше.
•    Строка кода BeginInvoke() выполняет функцию Foo(). Однако управление сразу возвращается вызывающему оператору без ожидания завершения Foo().
•    Код выше не знает, когда вызов Foo() завершится, это описано далее.
•    BeginInvoke() используется вместо Invoke(). Пока не беспокойтесь о параметрах, принимаемых этой функцией; они подробней описаны далее.