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

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

Аннотация: Асинхронное программирование в .NET Framework. Методы EndOperation, Pooling, Callback. Асинхронный запуск произвольного метода. Обновление интерфейса. Безопасность многопоточных приложений. Синхронизация: автоматическая, ручная; использование областей синхронизации. Элемент управления ProgressBar

Для работы с данной лекцией используйте примеры.

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

В этом модуле мы научимся создавать Windows-приложения, включающие в себя функциональность для предотвращения зависаний. Мы не можем устранить причины зависания, связанные с компьютером пользователя или его операционной системой, но хорошее проектирование приложения исключит зависания, связанные с ним самим.

Концепция асинхронного программирования

Главная идея асинхронного программирования заключается в том, чтобы запускать отдельные вызовы методов и параллельно продолжать выполнять другую работу без ожидания окончания вызовов.

Локальные методы, вероятность исключений которых сведена к минимуму, не нуждаются в асинхронном подходе (например, изменение цвета шрифта текста или его размера), но другие методы (ожидание чтения файла или запуск web-службы) требуют его в самом начале разработки. Среда Common Language Runtime поддерживает достаточно широкое количество методов и классов асинхронного программирования.

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

Условная схема работы процессора

увеличить изображение
Рис. 7.1. Условная схема работы процессора

Когда мы запускаем "долгий" метод, процессор будет выполнять его, при этом ему будет "некогда" выполнять другие запросы пользователя (рис. 7.2).

Обработка методов процессором

увеличить изображение
Рис. 7.2. Обработка методов процессором

При синхронном выполнении у приложения есть только один поток. С помощью асинхронной модели программирования вы можете запускать множество параллельных потоков и во время их выполнения реагировать на новые действия пользователя. После того как n-поток выполнен, вы отображаете результат на экран.

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

Поддержка асинхронного программирования в .NET Framework

Классы, в которых есть встроенная поддержка асинхронной модели, имеют пару асинхронных методов для каждого из синхронных методов. Эти методы начинаются со слов Begin и End. Например, если мы хотим воспользоваться асинхронным вариантом метода Read класса System.IO.Stream, нам нужно использовать методы BeginRead и EndRead этого же класса.

Для использования встроенной поддержки асинхронной модели программирования нужно вызвать соответствующий метод BeginOperation и выбрать модель завершения вызова. Вызов метода BeginOperation возвращает объект интерфейса IAsyncResult, с помощью которого определяется состояние выполнения асинхронной операции. Завершить выполнение асинхронных методов можно несколькими способами.

Метод EndOperation

Метод EndOperation применяется для завершения асинхронного вызова в тех случаях, когда основному потоку необходимо проделать большой объем вычислений, не зависящих от результатов вызова асинхронного метода. После того как основная работа сделана и приложение нуждается в результатах выполнения асинхронного метода для дальнейших действий, вызывается метод EndOperation. При этом основной поток будет приостановлен до завершения работы асинхронного метода. Пример использования этого способа:

using System;
using System.IO;

namespace EndOperation
{

 class Class1
  {
	
	[STAThread]
	static void Main(string[] args)
	{	
		//Создаем поток и открываем файл. 
		FileStream fs = new FileStream("text.txt", FileMode.Open);
		byte[] fileBytes = new byte[fs.Length];
		// Запуск метода Read в параллельном потоке.
		IAsyncResult ar = fs.BeginRead(fileBytes, 0, fileBytes.Length, null, null);
		for(int i = 0; i<10000000; i++)
		{
			// Имитация длительной работы основного
			// потока, не зависящая от выполнения асинхронного метода.
		}
		// После завершения работы основного потока
		// запускаем завершение выполнения параллельного 
		// метода Read.
		fs.EndRead(ar);
		Console.Write(System.Text.Encoding.Default.GetString(fileBytes));
	}
 }
}
Листинг 7.1.

Для завершения выполнения параллельного потока ar здесь был вызван метод EndRead. В качестве кода, имитирующего длительную работу, можно подключить точный счетчик выполнения задачи, разобранный нами в "Использование библиотек кода в windows-формах" .

Способ опроса (Pooling)

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

using System;
using System.IO;

namespace Pooling
{	
 class Class1
  {		
	[STAThread]
	static void Main(string[] args)
	{
		FileStream fs = new FileStream("text.txt", FileMode.Open);
		byte[] fileBytes = new byte[fs.Length];
		Console.Write("Выполнение метода Read асинхронно.");
		// Запуск метода Read асинхронно. 
		IAsyncResult ar = fs.BeginRead(fileBytes, 0, fileBytes.Length, null, null);			
		// Проверка на выполнение асинхронного метода.
		while(!ar.IsCompleted)
		{
			// Во время чтения файла на экран выводится надпись
			Console.Write("Процесс идет");
		}
		Console.WriteLine();
		string textFromFile = System.Text.Encoding.Default.GetString(fileBytes);
		Console.Write(textFromFile);
	}
 }
}
Листинг 7.2.

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

Способ Callback

Способ завершения асинхронного вызова Callback используется в тех случаях, когда нужно предотвратить блокирование основного потока. При использовании Callback мы запускаем метод EndOperation в теле метода, который вызывается при завершении метода, работающего в параллельном потоке. Сигнатура вызываемого метода должна совпадать с сигнатурой делегата AsyncCallback.

Пример использования варианта Callback:

using System;
using System.IO;

namespace Callback
{
	
	class Class1
	{		
		// Создаем поток и массив байтов.
		static FileStream fs;
		static byte[] fileBytes;
		[STAThread]
		static void Main(string[] args)
		{			
			// Открываем файл в поток.
			fs = new FileStream("text.txt", FileMode.Open);
			fileBytes = new byte[fs.Length];
			// Запускаем  метод Read асинхронно с передачей в качестве Сallback
			// метода WorkComplete
			fs.BeginRead(fileBytes, 0, (int)fs.Length, new AsyncCallback(WorkComplete), null);
		}
		/// <summary>
		/// Метод, вызываемый автоматически при завершении работы параллельного потока.
		/// </summary>
		/// <param name="ar">Объект типа IAsyncResult.</param>
		static void WorkComplete(IAsyncResult ar)
		{
			// Запуск окончания метода.
			fs.EndRead(ar);
			string textFromFile = System.Text.Encoding.Default.GetString(fileBytes);
			Console.Write(textFromFile);
		}
	}
}
Листинг 7.3.

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

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

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

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

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

Затем:

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

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

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

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

Анатолий Федоров
Анатолий Федоров
Россия, Москва, Московский государственный университет им. М. В. Ломоносова, 1989