|
При нажатии на Сумма в примере 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).

