Опубликован: 11.09.2006 | Уровень: специалист | Доступ: свободно
Лекция 7:

Асинхронное программирование

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

Рассмотрим теперь асинхронный запуск метода и его завершение несколькими способами в очень простом Windows-приложении. Создайте новое приложение и назовите его AsyncMethods. Добавляем на форму три кнопки и три надписи. Устанавливаем следующие свойства элементов управления:

button1, свойство Значение
Name btnEnd
Location 8; 16
Text End
button2, свойство Значение
Name btnIsComplete
Location 8; 56
Text IsComplete
button3, свойство Значение
Name btnCallback
Location 8; 96
Text Callback
label1, свойство Значение
Name lblEnd
Location 104; 16
Size 168; 23
Text
label2, свойство Значение
Name lblIsComplete
Location 104; 56
Size 168; 23
Text
label3, свойство Значение
Name lblCallback
Location 104; 96
Size 168; 23
Text

Переходим в код формы. Создаем делегат 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 метод будет возвращать одну из трех переменных типа stringCallback, 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

Рис. 7.4. Приложение AsyncMethods

На диске, прилагаемом к книге, вы найдете приложение AsyncMethods (Code\Glava7\AsyncMethods).

Обновление пользовательского интерфейса в Windows-приложениях

Обновление пользовательского интерфейса само по себе является задачей, которую нужно решать, используя приемы асинхронного программирования. Дело в том, что вид приложения может зависеть от внешних процессов — например, от соединения с удаленным сервером или базой данных. Рассмотрим общую схему обновления на простейшем примере добавления единственной кнопки в режиме запуска приложения.

Создайте новое Windows-приложение и назовите его TestMethodInvoker. Перетаскиваем на форму три кнопки (точное расположение их не важно) и свойству Name устанавливаем значения btnAddSync, btnAsync, btnAddAsyncMI (в соответствии со свойством Text на рис. 7.5).

Приложение TestMethodInvoker в режиме дизайна

Рис. 7.5. Приложение TestMethodInvoker в режиме дизайна

В 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).

Елена Дьяконова
Елена Дьяконова

При нажатии на Сумма в примере ArbitraryMethod из Лекция 7, VS 2013 выдается ошибка: 

Необработанное исключение типа "System.InvalidOperationException" в System.Windows.Forms.dll

Дополнительные сведения: Недопустимая операция в нескольких потоках: попытка доступа к элементу управления "lblResult" не из того потока, в котором он был создан.

Затем:

Необработанное исключение типа "System.InvalidOperationException" в mscorlib.dll

Дополнительные сведения: Для каждой асинхронной операции метод EndInvoke может вызываться только один раз.

Александр Сороколет
Александр Сороколет

Свойство WindowState формы blank Maximized. Не открывается почемуто на всё окно, а вот если последующую форму бланк открыть уже на макс открывается :-/