При нажатии на Сумма в примере ArbitraryMethod из Лекция 7, VS 2013 выдается ошибка: Необработанное исключение типа "System.InvalidOperationException" в System.Windows.Forms.dll Дополнительные сведения: Недопустимая операция в нескольких потоках: попытка доступа к элементу управления "lblResult" не из того потока, в котором он был создан. Затем: Необработанное исключение типа "System.InvalidOperationException" в mscorlib.dll Дополнительные сведения: Для каждой асинхронной операции метод EndInvoke может вызываться только один раз. |
Асинхронное программирование
Асинхронный запуск метода с различными вариантами завершения вызова
Рассмотрим теперь асинхронный запуск метода и его завершение несколькими способами в очень простом Windows-приложении. Создайте новое приложение и назовите его AsyncMethods. Добавляем на форму три кнопки и три надписи. Устанавливаем следующие свойства элементов управления:
Переходим в код формы. Создаем делегат AsynchronousMethodDelegate:
private delegate string AsynchronousMethodDelegate(ActivateType source);
Создаем метод, предназначенный для асинхронного запуска:
private string AsynchronousMethod(ActivateType source) { string result = String.Empty; switch(source) { case ActivateType.Callback: result = "Callback"; break; case ActivateType.End: result = "End"; break; case ActivateType.IsComplete: result = "Is Complete"; break; } return result; }
В зависимости от выбора значения перечисления ActivateType метод будет возвращать одну из трех переменных типа string — Callback, End или Is Complete. Выбранное значение перечисления будет типом завершения вызова:
private enum ActivateType { End, IsComplete, Callback }
В обработчике кнопки btnEnd выводим результат работы асинхронного метода на надпись lblEnd:
private void btnEnd_Click(object sender, System.EventArgs e) { AsynchronousMethodDelegate asyncDeleg = new AsynchronousMethodDelegate(AsynchronousMethod); // Запускаем асинхронно метод AsynchronousMethod. IAsyncResult ar = asyncDeleg.BeginInvoke(ActivateType.End, null, null); // Получаем результаты асинхронного метода. lblEnd.Text = asyncDeleg.EndInvoke(ar); }Листинг 7.7.
В обработчике кнопки btnIsComplete применяем способ завершения работы Pooling и также выводим результат на надпись lblIsComplete:
private void btnIsComplete_Click(object sender, System.EventArgs e) { AsynchronousMethodDelegate asyncDeleg = new AsynchronousMethodDelegate(AsynchronousMethod); // Запускаем асинхронно метод AsynchronousMethod. IAsyncResult ar = asyncDeleg.BeginInvoke(ActivateType.IsComplete, null, null); while(!ar.IsCompleted) { lblIsComplete.Text = "Ожидание"; } // Получаем результаты асинхронного метода. lblIsComplete.Text = asyncDeleg.EndInvoke(ar); }Листинг 7.8.
В обработчике кнопки применяем способ завершения работы Callback:
private void btnCallback_Click(object sender, System.EventArgs e) { AsynchronousMethodDelegate asyncDeleg = new AsynchronousMethodDelegate(AsynchronousMethod); // Создаем экземпляр callback делегата Callback. AsyncCallback callback = new AsyncCallback(CallbackMethod); // Запускаем асинхронно метод AsynchronousMethod. asyncDeleg.BeginInvoke(ActivateType.Callback, callback, asyncDeleg); }Листинг 7.9.
Добавляем метод для завершения вызова и выводим результат на надпись lblCallback:
private void CallbackMethod(IAsyncResult ar) { // Приводим к типу AsynchronousMethodDelegate AsynchronousMethodDelegate asyncDeleg = (AsynchronousMethodDelegate)ar.AsyncState; // Получаем результаты асинхронного метода. lblCallback.Text = asyncDeleg.EndInvoke(ar); }Листинг 7.10.
Запускаем приложение. Его работа предельно проста — при нажатии на кнопки запускается метод AsynchronousMethod, который завершается одним из асинхронных способов (рис. 7.4).
На диске, прилагаемом к книге, вы найдете приложение AsyncMethods (Code\Glava7\AsyncMethods).
Обновление пользовательского интерфейса в Windows-приложениях
Обновление пользовательского интерфейса само по себе является задачей, которую нужно решать, используя приемы асинхронного программирования. Дело в том, что вид приложения может зависеть от внешних процессов — например, от соединения с удаленным сервером или базой данных. Рассмотрим общую схему обновления на простейшем примере добавления единственной кнопки в режиме запуска приложения.
Создайте новое Windows-приложение и назовите его TestMethodInvoker. Перетаскиваем на форму три кнопки (точное расположение их не важно) и свойству Name устанавливаем значения btnAddSync, btnAsync, btnAddAsyncMI (в соответствии со свойством Text на рис. 7.5).
В Windows-приложениях перед обновлением пользовательского интерфейса необходимо вернуть элемент управления в основной поток. Это делается с помощью делегата MethodInvoker, который не принимает и не возвращает параметров.
Чтобы инициализировать MethodInvoker-делегат, нужно создать метод, обновляющий пользовательский интерфейс:
// Метод, обновляющий пользовательский интерфейс private void UpdateUI() {} // Инициализация делегата MethodInvoker MethodInvoker methodinvoker = new MethodInvoker(UpdateUI);
Для запуска экземпляра methodinvoker делегата MethodInvoker асинхронно — вызываем метод BeginInvoke текущей формы:
// Асинхронный запуск this.BeginInvoke(methodinvoker);
Добавляем обработчиков кнопок:
private void btnAddSync_Click(object sender, System.EventArgs e) { // Синхронный запуск метода UpdateUI UpdateUI(); } // Делегат для вызова метода UpdateUI асинхронно. delegate void AddItemAsyncDelegate(); private void btnAddAsyncMI_Click(object sender, System.EventArgs e) { // Асинхронный вызов метода UpdateAsync AddItemAsyncDelegate asyncDel = new AddItemAsyncDelegate(UpdateAsync); asyncDel.BeginInvoke(null, null); } private void UpdateAsync() { // Асинхронный вызов метода UpdateUI MethodInvoker methodinvoker = new MethodInvoker(UpdateUI); this.BeginInvoke(methodinvoker); } private void UpdateUI() { // Добавление элемента управления. Button btn = new Button(); btn.Text = "Кнопка"; this.Controls.Add(btn); } private void btnAsync_Click(object sender, System.EventArgs e) { // Асинхронный вызов метода UpdateUI // При выполнении этого метода элемент управления на форму // не добавится, поскольку он создан не в основном потоке. AddItemAsyncDelegate asyncDel = new AddItemAsyncDelegate(UpdateUI); asyncDel.BeginInvoke(null, null); }Листинг 7.11.
Запускаем приложение. При нажатии кнопки Sync кнопка появляется самым обычным способом — в ее обработчике просто вызывается метод UpdateUI. В обработчиках кнопок Async и Async with MI вызывается метод UpdateUI, только в первом случае элемент создается не в основном потоке, поэтому не происходит обновления пользовательского интерфейса, а во втором случае элемент возвращается в основной поток – и кнопка появляется на форме.
Использование делегата MethodInvoker необходимо только в Windows-приложениях.
На диске, прилагаемом к книге, вы найдете приложение TestMethodInvoker (Code\Glava7\ TestMethodInvoker).