Выбрать главу

    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();

 //Упаковать точку входа метода класса в делегат

 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);

}

Потоки и пользовательский интерфейс

Время от времени приходится слышать вопрос: "Следует ли использовать несколько потоков для выполнения пользовательского интерфейса?" На этот вопрос почти однозначно должен быть дан отрицательный ответ. Привлекать для управления различными частями пользовательского интерфейса несколько потоков практически никогда не имеет смысла. Это особенно относится к мобильным устройствам, в которых пользовательские интерфейсы приложения, как правило, занимают весь экран.