Программирование мобильных устройств на платформе .NET Compact Framework - Иво Салмре
Шрифт:
Интервал:
Закладка:
//Рабочие состояния
//-----------------
//Ожидание запуска фонового потока
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) {
//Создать объект, который мы можем использовать
//в конечном автомате в целях блокировки
m_useForStateMachineLock = new Object();
//Обозначить готовность к началу выполнения
m_processingState = ProcessingState.notYetStarted;
//Сохранить функцию, которую необходимо вызвать
//в новом потоке
m_CallFunction = functionToCall;
//----------------------------------------------------------
//Создать новый поток и вызвать в нем функцию на выполнение:
// this.ThreadStartPoint()
//----------------------------------------------------------
System.Threading.ThreadStart threadStart;
threadStart = new System.Threading.ThreadStart(ThreadStartPoint);
System.Threading.Thread newThread;
newThread = new System.Threading.Thread(threadStart);
//Обозначить готовность к началу выполнения (в целях определенности
//это важно сделать еще до того, как будет запущен поток!)
setProcessingState(ProcessingState.waitingToStartAsync);
//Дать ОС команду начать выполнение нового потока в асинхронном режиме
newThread.Start();
//Возвратить управление функции, вызывающей этот поток
}
//---------------------------------------------
//Эта функция является точкой входа, вызываемой
//для выполнения в новом потоке
//---------------------------------------------
private void ThreadStartPoint() {
//Установить состояние обработки, соответствующее
//выполнению функции в новом потоке!
setProcessingState(ProcessingState.running);
//Запустить на выполнение пользовательский код и передать указатель в
//наш класс, чтобы этот код мог периодически проверять, не поступил ли
//запрос на прекращение выполнения
m_CallFunction(this);
//Если выполнение не было отменено, изменить состояние таким образом,
//чтобы оно соответствовало успешному завершению
if (m_processingState != ProcessingState.aborted) {
//Обозначить завершение выполнения
setProcessingState(ProcessingState.done);
}
//Выйти из потока...
}
//----------------
//Конечный автомат
//----------------
public void setProcessingState(ProcessingState nextState) {
//B любой момент времени только одному потоку выполнения
//могут быть разрешены попытки изменить состояние
lock(m_useForStateMachineLock) {
//B случае попытки повторного вхождения в текущее состояние
//никакие дополнительные действия не выполняются
if (m_processingState == nextState) {
return;
}
//------------------------------------------------------
//Простейший защитный код, гарантирующий
//невозможность перехода в другое состояние, если задача
//либо успешно завершена, либо успешно отменена
//------------------------------------------------------
if ((m_processingState == ProcessingState.aborted) ||
(m_processingState == ProcessingState.done)) {
return;
}
//Убедиться в допустимости данного изменения состояния
switch (nextState) {
case ProcessingState.notYetStarted:
throw new Exception("Переход в состояние 'notYetStarted' невозможен");
case ProcessingState.waitingToStartAsync:
if (m_processingState != ProcessingState.notYetStarted) {
throw new Exception("Недопустимое изменение состояния");
}
break;
case ProcessingState.running:
if (m_processingState != ProcessingState.waitingToStartAsync) {
throw new Exception("Недопустимое изменение состояния");
}
break;
case ProcessingState.done:
//Мы можем завершить работу лишь тогда, когда она выполняется.
//Это возможно также в тех случаях, когда пользователь затребовал
//отмену выполнения, но работа к этому моменту уже была закончена
if ((m_processingState != ProcessingState.running) &&
(m_processingState != ProcessingState.requestAbort)) {
throw new Exception("Недопустимое изменение состояния");
}
break;
case ProcessingState.aborted:
if (m_processingState != ProcessingState.requestAbort) {
throw new Exception("Недопустимое изменение состояния");
}
break;
}
//Разрешить изменение состояния
m_processingState = nextState;
}
}
public ProcessingState State {
get {
ProcessingState currentState;
//Предотвратить попытки одновременного чтения/записи состояния
lock(m_useForStateMachineLock) {
currentState = m_processingState;
}
return currentState;
}
}
} //Конец класса
В листинге 9.2 представлен код, имитирующий выполнение работы фоновым потоком. Когда фоновый поток начинает выполнять код, на экране отображается окно сообщения. Выполнение работы имитируется созданием серии пауз длительностью в одну треть секунды, в промежутках между которыми рабочий код проверяет, не поступил ли от другого потока запрос на прекращение выполнения.
Листинг 9.2. Тестовая программа для выполнения работы в фоновом потокеusing System;
//-------------------------------------------------
//Тестовый код, который используется для выполнения
//фоновым потоком
//-------------------------------------------------
public class Test1 {
public int m_loopX;
//------------------------------------------------------------------
//Функция, вызываемая фоновым потоком
// [in] threadExecute: Класс, управляющий выполнением нашего потока.
// Мы можем контролировать его для проверки
// того, не следует ли прекратить вычисления
//------------------------------------------------------------------
public void ThreadEntryPoint(ThreadExecuteTask threadExecute) {
//Это окно сообщений будет отображаться в контексте того потока,
//в котором выполняется задача
System.Windows.Forms.MessageBox.Show("Выполнение ТЕСТОВОГО ПОТОКА");
//------
//60 раз
//------
for (m_loopX = 0; m_loopX < 60; m_loopX++) {
//Если затребована отмена выполнения, мы должны завершить задачу
if (threadExecute.State == ThreadExecuteTask.ProcessingState.requestAbort) {
threadExecute.setProcessingState(ThreadExecuteTask.ProcessingState.aborted);
return;
}
//Имитировать выполнение работы: пауза 1/3 секунды
System.Threading.Thread.Sleep(333);
}
}
} //Конец класса
В листинге 9.3 содержится код, который можно запустить на выполнение из основного потока пользовательского интерфейса с целью активизации и контроля фоновой обработки. Этот код не является независимым классом и должен помещаться в форму, с кнопками которой должны быть связаны события щелчков на кнопках.
Листинг 9.3. Код для запуска и тестирования приведенного выше тестового кода//Класс, который будет управлять выполнением нового потока
private ThreadExecuteTask m_threadExecute;
//Класс, метод которого мы хотим выполнять в асинхронном режиме
Test1 m_testMe;
//----------------------------------------------------------
//Этот код должен быть запущен ранее другого кода, поскольку
//он запускает новый поток выполнения!
//
//Создать новый поток и обеспечить его выполнение
//----------------------------------------------------------
private void buttonStartAsyncExecution_Click(object sender, System.EventArgs e) {
//Создать экземпляр класса, метод которого мы хотим вызвать
//в другом потоке
m_testMe = new Test1();