Заблаговременное выполнение части работы, в которой заинтересован пользователь, может требовать от вас определенного мастерства, но и положительный эффект от этого может оказаться значительным. Если этот подход реализован так, как следует, то пользователям почти никогда не представится случая по достоинству оценить, какая громадная работа для этого вами проделана; точно так же здоровый человек почти никогда не обращает внимания на то, как работает его сердце.
В качестве хорошего примера можно привести программу для просмотра изображений в Windows XP. Окно этой программы появляется сразу же после того, как вы дважды щелкнете мышью на имени файла изображения в окне проводника. В результате этого изображение загружается и отображается на экране. Одновременно с этим, но незаметно для пользователя
и без какой-либо инициативы с его стороны, после вывода на экран первого изображения на фоне загружается также изображение, файл которого размещен в каталоге вслед за первым. Если следующим действием пользователя, которое можно считать наиболее вероятным, будет щелчок на кнопке Next Image (Следующее изображение) в программе для просмотра изображений, то следующая фотография незамедлительно появится на экране без видимой задержки. В случае современных крупных цифровых фотографий, загрузка, распаковка и масштабирование которых требуют значительного времени, это не такое уж и малое достижение. Описанная заблаговременная загрузка производится в основном только для изображений, которые располагаются следующими в списке файлов каталога изображений. Если вы щелкнете на кнопке Previous Image (Предыдущее изображение), то, вероятнее всего, увидите на экране сообщение "Generating preview" ("Генерируется изображение предварительного просмотра"), появляющееся на короткое время на экране при загрузке фотографии по требованию. Переходы в обратном направлении используются менее часто и поэтому не оптимизируются. Поскольку для цифровых изображений могут требоваться значительные ресурсы памяти, осуществление предварительной загрузки большого количества изображений в предвидении того, что пользователь может захотеть их просмотреть, на сегодняшний день вызывает затруднения. Аналогичным образом, то же самое сообщение "Generating preview" будет появляться на экране, если вы выполните ряд быстрых последовательных щелчков на кнопке Next Image, требующих загрузки очередных изображений; это объясняется тем, что вы требуете большего, чем позволяют возможности средств опережающего просмотра в отношении загрузки следующих фотографий. В большинстве случаев, прежде чем переходить к следующей фотографии, текущую фотографию рассматривают хотя бы в течение одной-двух секунд, и именно для такого сценария и была предусмотрена оптимизация.
Оптимизация загрузки изображений программой просмотра основывается на вполне разумных предположениях:
1. В большинстве случае порядок просмотра фотографий соответствует продвижению по списку в прямом направлении.
2. Обычно каждую фотографию рассматривают достаточно долго для того, чтобы система успела выполнить фоновую загрузку следующей фотографии.
Независимо от того, используется ли в программе просмотра изображений Windows XP фоновый поток для решения этой задачи или не используется (мне это неизвестно), она демонстрирует пример разумного применения асинхронной обработки для оптимизации рабочего процесса в случае наиболее распространенных действий. Прекрасная работа!
Пример использования фонового потока для выполнения отдельной задачи
Показанный в листинге 9.1 код представляет класс, который позволяет управлять выполнением задачи в фоновом потоке. Для отслеживания состояний подготовки приложения к выполнению, запуска нового потока, выполнения кода потоком и выхода из потока выполнения по завершении работы применяется конечный автомат.
Кроме того, в рассматриваемом примере основному потоку предоставляется возможность запрашивать прекращение выполнения фоновой задачи. Для уведомления потока, выполняющего фоновую задачу, о поступлении запроса на прекращение выполнения, используется вызов метода m_threadExecute.setProcessingState(ThreadExecuteTask.ProcessingState.requestAbort) из другого потока. За периодическую проверку этого состояния и осуществление возможного прекращения выполнения операции отвечает код, выполняемый фоновым потоком. Конечный автомат для класса ThreadExecuteTask представлен на рис. 9.1.
Рис. 9.1. Конечный автомат для отдельной задачи, выполняемой фоновым потоком
Листинг 9.1. Код для управления выполнением одиночной задачи фоновым потоком
using System;
public class ThreadExecuteTask {
//Перечисляем возможные состояния
public enum ProcessingState {
//-------------------
//Начальное
состояние
//-------------------
//Пока ничего интересного не происходит
notYetStarted,
//-----------------
//Рабочие состояния
//-----------------
//Ожидание запуска фонового потока
waitingToStartAsync,
//Выполнение кода в фоновом потоке
running,
//Запросить отмену выполнения вычислений
requestAbort,
//--------------------
//Состояния завершения
//--------------------
//Состояние завершения: выполнение фонового потока
//успешно завершено
done,
//Состояние завершения: выполнение потока отменено
//до его завершения
aborted
}
ProcessingState m_processingState;
public delegate void ExecuteMeOnAnotherThread(ThreadExecuteTask checkForAborts);
private ExecuteMeOnAnotherThread m_CallFunction;
private object m_useForStateMachineLock;
public ThreadExecuteTask(ExecuteMeOnAnotherThread functionToCall) {