Программирование мобильных устройств на платформе .NET Compact Framework - Иво Салмре
Шрифт:
Интервал:
Закладка:
m_testMe = new Test1();
//Упаковать точку входа метода класса в делегат
ThreadExecuteTask.ExecuteMeOnAnotherThread delegateCallCode;
delegateCallCode = new ThreadExecuteTask.ExecuteMeOnAnotherThread(m_testMe.ThreadEntryPoint);
//Дать команду начать выполнение потока!
m_threadExecute = new ThreadExecuteTask(delegateCallCode);
}
//Проверить состояние выполнения
private void buttonCheckStatus_Click(object sender, System.EventArgs e) {
//Запросить у класса управления потоком, в каком состоянии он находится
System.Windows.Forms.MessageBox.Show(m_threadExecute.State.ToString());
//Запросить класс, метод которого выполняется в потоке,
//о состоянии выполнения
System.Windows.Forms.MessageBox.Show(m_testMe.m_loopX.ToString());
}
//Принудительно вызвать запрещенное изменение состояния
//(это приведет к возбуждению исключения)
private void buttonCauseException_Click(object sender, System.EventArgs e) {
m_threadExecute.setProcessingState(ThreadExecuteTask.ProcessingState.notYetStarted);
}
//Послать асинхронному коду запрос с требованием отмены его выполнения
private void buttonAbort_Click(object sender, System.EventArgs e) {
m_threadExecute.setProcessingState(ThreadExecuteTask.ProcessingState.requesAbort);
}
Потоки и пользовательский интерфейс
Время от времени приходится слышать вопрос: "Следует ли использовать несколько потоков для выполнения пользовательского интерфейса?" На этот вопрос почти однозначно должен быть дан отрицательный ответ. Привлекать для управления различными частями пользовательского интерфейса несколько потоков практически никогда не имеет смысла. Это особенно относится к мобильным устройствам, в которых пользовательские интерфейсы приложения, как правило, занимают весь экран.
Обычно окна связываются с потоком, который является их владельцем; это справедливо для операционных систем Windows СЕ, Pocket PC и Microsoft Smartphone (а также для таких настольных операционных систем, как Windows XP и более ранние версии Windows). Для каждого окна имеется поток, которому оно принадлежит и который им управляет. Один и тот же поток может владеть несколькими окнами. Поток играет роль "генератора сообщений" по отношению к этим окнам и пересылает им сообщения, когда окно должно быть перерисовано, когда нажимается клавиша, когда выполняется щелчок на кнопке и так далее.
Хотя и можно организовать приложение таким образом, чтобы пользовательский интерфейс обслуживался несколькими потоками (например, по одному потоку на одно окно верхнего уровня), это почти никогда не принесет никакой пользы. Это только усложняет структуру приложения, ничуть не ускоряя его работу. Если вы считаете, что для пользовательского интерфейса необходимо задействовать несколько потоков, задайте себе вопрос, а для чего это в действительности вам надо и нельзя ли при этом использовать один основной поток пользовательского интерфейса и несколько рабочих фоновых потоков, что сделает модель гораздо более понятной.
В подавляющем большинстве случаев намерением использовать несколько потоков, каждый из которых связан с пользовательским интерфейсом, движет желание обеспечить постоянную информированность пользователя о состоянии задач, выполняемых различными фоновыми потоками. Гораздо лучше периодически запрашивать эти данные при помощи одного высокоприоритетного потока интерфейса, используя таймер, чем управлять несколькими окнами на экране посредством нескольких потоков. Проектирование фоновых задач в виде классов, использующих конечные автоматы, упрощает получение соответствующих данных от потока пользовательского интерфейса. Вот вам еще один веский довод в пользу применения подходов, основанных на использовании конечных автоматов.
Даже если ваша оконная модель и не связана с какими-либо специфическими потоками, обычно целесообразнее иметь только один поток выполнения, который "берет на себя попечительство" над пользовательским интерфейсом.
Работая в .NET Compact Framework, не пытайтесь получить доступ к элементам управления пользовательского интерфейса из потоков, которым они не принадлежатНи вариант .NET Framework для настольных компьютеров, ни вариант .NET Compact Framework для мобильных устройств не поддерживают доступ к большинству свойств и методов элементов пользовательского интерфейса из потоков, которым они не принадлежат. Хотя такой код и будет нормально компилироваться, результаты его выполнения будут непредсказуемыми. Для обеспечения межпоточных вызовов в .NET Framework и .NET Compact Framework поддерживается метод Control.Invoke(). В версии 1.1 .NET Compact Framework поддерживается лишь использование механизма Control.Invoke() для вызова функций без параметров. Более подробную информацию относительно применения этого метода вы найдете в справочной документации MSDN. Не составляет труда организовать на приемлемом уровне обмен данными между фоновым потоком и потоком пользовательского интерфейса, предусмотрев для этого выполняющийся в потоке пользовательского интерфейса код, который периодически опрашивает объект, специально предназначенный для управления выполнением фоновых потоков, с целью определения того, имеются ли данные, ожидающие реакции пользовательского интерфейса. Обычно сделать это гораздо проще, чем погружаться во все тонкости межпоточного вызова методов.
Второй подход связан с использованием косвенно вызываемого делегата, указывающего на функцию формы вашего приложения. В качестве такой функции (не имеющей параметров — см. выше) может быть назначена функция класса формы, которую можно вызвать посредством метода Invoke() формы. Вызов Invoke() приведет к выполнению упомянутой функции в потоке пользовательского интерфейса. После этого функция может извлечь любые необходимые данные и соответствующим образом обновить пользовательский интерфейс. Достоинством такого подхода является то, что высокоприоритетный поток не должен выполнять цикл опроса и получает обновленную информацию сразу же после завершения фоновой работы. Недостаток этого подхода заключается в том, что он требует создания (предположительно, на очень короткое время) синхронной связи между фоновым и высокоприоритетным потоками выполнения. Когда делегат запускается на выполнение в фоновом потоке, выполнение фонового потока приостанавливается, контекст выполнения переключается на высокоприоритетный поток, и делегат начинает выполняться. Это препятствует переключению фонового потока на выполнение другой работы, находящейся в очереди. Выполнение фонового потока сможет возобновиться лишь после того, как выполнение делегата завершится.
Пример использования фоновой обработки одновременно с обновлением данных высокоприоритетного потока пользовательского интерфейса
Сейчас мы вернемся к нашему примеру с простыми числами, который рассматривался в главе 5, заметно изменив и усовершенствовав его. На этот раз мы создадим приложение типа SmartPhone, вычисляющее простые числа большой величины. Во время проведения большого объема вычислений фоновым потоком приложение будет сохранять способность к интерактивному взаимодействию с пользователем. Приложение предоставляет пользователю возможность при необходимости прекратить выполнение фонового потока. Кроме того, и это немаловажно, в приложении реализован неплохой способ информирования пользователя о состоянии выполнения интересующей его задачи. Зная, что фоновая задача успешно выполняется, пользователь будет чувствовать себя более комфортно
Данное приложение несложно адаптировать для выполнения на Pocket PC. Наш выбор Microsoft Smartphone в качестве целевой платформы был сделан исключительно в интересах разнообразия.
Рис. 9.2. Окно интегрированной среды разработки Visual Studio, предcтавляющее проектируемый пользовательский интерфейс приложения типа Smartphone
Рис. 9.3. Экранные снимки эмулятора Smartphone, полученные в процессе вычисления приложением простых чисел
НА ЗАМЕТКУ
Если вы используете Visual Studio .NET 2003, то вам необходимо загрузить SDK для Windows Mobile 2003-based Smartphones. Visual Studio NET 2003 поставлялась с "коробочным" вариантом средств разработки приложений для Pocket PC, но не для Smartphone. Поскольку SDK для Smartphone поставлялся после выхода Visual Studio .NET 2003, его следует загрузить и установить поверх Visual Studio .NET. Пакет SDK можно бесплатно загрузить с Web- сайта компании Microsoft (см. приложение А). Этот SDK включает в себя компоненты, необходимые для проектирования пользовательских интерфейсов Smartphone, а также эмулятор Smartphone, позволяющий выполнять приложения, даже если вы не располагаете физическим устройством Smartphone.
Чтобы создать и запустить указанное приложение, выполните следующие действия: