ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание
Шрифт:
Рис. 14.1. Синхронные вызовы методов "блокируют" другие вызовы
При выполнении этой программы вы заметите, что перед тем выполнением Console.WriteLine произойдет пятисекундная задержка. И хотя многие методы (если не подавляющее их большинство) могут вызваться синхронно совершенно безболезненно, делегатам .NET, если это необходимо, можно дать указание вызывать методы асинхронно.
Исходный код. Проект SyncDelegate размещен в подкаталоге, соответствующем главе 14.
Асинхронная природа делегатов
Если для вас тема многопоточных приложений является
Но как дать указание делегату вызвать метод в отдельном потоке выполнения, чтобы имитировать одновременное выполнение множества задач? К счастью, нужной для этого способностью автоматически наделяется каждый тип делегата .NET. И более того, для такого вызова вам не требуется углубляться в детали пространства имен System.Threading (хотя, естественно, одно другому не мешает).
Методы BeginInvoke и EndInvoke
Когда компилятор C# обрабатывает ключевое слово delegate, динамически генерируемый класс определяет два метода с именами BeginInvoke и EndInvoke. Для нашего определения делегата BinaryOp эти методы оказываются следующими.
Первый набор параметров, передаваемых в BeginInvoke, формируется на основе формата делегата C# (в случае BinaryOp это два целочисленных значения). Последними двумя аргументами всегда являются System.AsyncCallback и System.Object. Мы рассмотрим роль этих параметров чуть позже, а пока что для каждого из них мы будем использовать null.
Интерфейс System.IAsyncResult
Метод BeginInvoke всегда возвращает объект, реализующий интерфейс IAsyncResult, а метод EndInvoke имеет единственный параметр типа IAsyncResult. Совместимый с IAsyncResult объект, возвращаемый методом BeginInvoke, и является тем связующим механизмом, который позволяет вызывающему потоку получить результат асинхронного вызова метода позже с помощью EndInvoke. Интерфейс IAsyncResult (определенный в пространстве имен System) задается так, как показано ниже.
В самом простом случае
можно избежать непосредственного вызова этих членов. Требуется только сохранить совместимый с IAsyncResult объект, возвращенный BeginInvoke, и передать его методу EndInvoke, когда вы будете готовы получить результат вызова метода. Позже вы увидите, что у вас есть возможность вызывать члены совместимого с IAsyncResult объекта, если вы хотите "участвовать" в процессе извлечения возвращаемого значений метода.Замечание. Если асинхронно вызывается метод, который не предлагает возвращаемых значений, можно его вызвать и престо "забыть" о нем. В таких случаях нет необходимости сохранять совместимый с IAsyncResult объект и вызывать EndInvoke (так как нет возвращаемого значения, которое требуется извлечь).
Асинхронный вызов методов
Чтобы дать указание делегату BinaryOp вызвать метод Add асинхронно, измените предыдущий метод Main так, как показано ниже.
Выполнив это приложение, вы увидите, что теперь выводятся два разных хешированных значения, поскольку в границах текущего домена приложения выполняются два потока (см. рис. 14.2).
Рис. 14.2. Методы, вызываемые асинхронно, выполняют свою работу в отдельном потоке
Вдобавок к уникальным хешированным значениям, вы также обнаружите, что при запуске приложения сообщение "В Main еще есть работа!" появляется практически немедленно.
Синхронизация вызывающего потока
Для текущей реализации Main диапазон времени между вызовом BeginInvoke и вызовом EndInvoke явно меньше пяти секунд. Поэтому после вывода на консоль сообщения "В Main еще есть работа!" поток вызова блокируется и ждет завершения существования вторичного потока, который должен получить результат метода Add. Таким образом, вы на самом деле выполняете еще один синхронный вызов.