Создание рабочего потока

   
На этом шаге мы рассмотрим алгоритм создания рабочего потока.

   
Для рабочего потока необходимо написать код управляющей функции, предназначенной для реализации основной
задачи потока. Ее адрес передается в качестве параметра функции AfxBeginThread(). Управляющая функция
имеет следующий синтаксис:

    UINT MyControllingFunction(LPVOID pParam);

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

   
Объявление соответствующей версии функции AfxBeginThread() имеет следующий вид:

  CWinThread* AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam,
  int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, 
  DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);

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

   
Функция AfxBeginThread() создает новый объект CWinThread, вызывает функцию CreateThread()
для запуска потока и возвращает указатель на него. В ходе этой процедуры проводятся проверки, подтверждающие правильность
освобождения всех объектов при возникновении сбоя на каком-либо этапе создания. Завершается поток самостоятельно,
если функция потока содержит оператор return или при вызове глобальной функции AfxEndThread().
Возвращаемое значение в общем случае указывает на причину завершения. По существующей традиции при успешном
завершении функция возвращает ноль, а ненулевое значение содержит код ошибки.

   
Проиллюстрируем создание рабочего потока для приложения МуАрр. Создадим простую функцию таймера,
предназначенную для вывода информационного окна по достижению за данного времени. Пользователь настроит
таймер в специальном диалоговом окне, после чего каждую секунду рабочий поток будет проверять системные часы.
По достижении заданного интервала поток выведет информационное окно и завершит работу.

   
Создадим класс CTimer, инкапсулирующий таймер. В этом классе для хранения времени будет создана
защищенная MFC-переменная CTime и открытый указатель CWinThread, позволяющий oпределить,
ссылается ли объект CTimer на активный поток.

  • Создание класса CTimer.
  • Откройте проект МуАрр. На вкладке FileView дважды щелкните значок файла MainFrm.cpp.
  • В начало файла после операторов #include добавьте следующий код:
    class CTimer 
    {
      protected: 
        CTime m_time;
      public:
        CWinThread *m_thread;
        CTimer()  {Reset();}
        CTime GetTime()  {return m_time; }
        void    SetTime(CTime time)  {m_time = time;}
        void   Reset()     {
             m_time = CTime::GetCurrentTime();
             m_thread = NULL; } 
    };
    
    


    Рис.1. Описание класса CTimer
    Функция GetCurrentTime(), будучи статическим членом класса CTime, возвращает текущее системное
    время в формате CTime.

  • Непосредственно под объявлением класса добавьте объявление глобального объекта CTimer:
      CTimer g_timer;
    

   
Далее создадим функцию рабочего потока, которая с интервалом в одну секунду будет сравнивать системное время и
время таймера. При их совпадении функция выведет информационное окно и переустановит таймер.

  • Добавление функции DoTimer().
  • В файле MainFrm.cpp непосредственно под объявлением класса поместите следующее описание функции:
    UINT DoTimer(LPVOID pparam)
    {
      CTime currenttime = CTime::GetCurrentTime(); 
      while(currenttime < g_timer.GetTime())
      {
        Sleep(1000);
        currenttime = CTime::GetCurrentTime();
      }
      AfxMessageBox("Time's up!"); 
      g_timer.Reset();
      return 0; 
    }
    

    Функция Sleep() переводит поток, в котором она вызвана, в состояние ожидания на заданное число миллисекунд.


    Рис.2. Добавление функции DoTimer()

  • Сейчас подготовим диалоговое окно для установки времени таймера. Посредством редактора диалогов создайте
    диалоговое окно.


    Рис.3. Создание диалогового окна
    Установите для диалогового окна идентификатор IDD_TIMER. Элемент управления, похожий на поле со
    списком, доступен на панели инструментов элементов управления под названием Date Time Picker. Задайте для
    него идентификатор IDC_DTPSETTIME и свойство, в соответствии с которым он будет показывать только время.

  • Настройка элемента управления IDC_DTPSETTIME для показа только времени.
  • В редакторе диалогов выберите элемент управления IDC_DTPSETTIME. Нажмите ENTER, чтобы
    изменить его свойства.
  • Щелкните вкладку Styles. В списке Format выберите Time.
  • Щелкните за пределами окна свойств, чтобы закрыть его.
  • Теперь создадим класс диалога для диалогового окна Timer.

  • Создание класса CTimerDialog.
  • В редакторе диалогов нажмите CTRL+W, чтобы открыть СlassWizard. Создайте класс CTimerDialog.
  • Щёлкните вкладку Member Variables. Создайте переменную-член категории Value для
    идентификатора IDC_DTPSETTIME. Это должна быть переменная типа CTime с именем m_settime.


    Рис.4. Добавление переменной m_settime

  • Щелкните ОК, чтобы закрыть ClassWizard.
  • Теперь осталось лишь добавить команду и функцию-обработчик для установки таймера и запуска потока таймера.

  • Добавление в меню View пункта Timer
    и создание функции-обработчика OnViewTimer().
  • С помощью редактора меню создайте в меню View команду Timer.
  • Подтвердите идентификатор ID_VIEW_TIMER, предлагаемый по умолчанию.
  • В ClassWizard на вкладке Message Maps создайте обработчик команды для объекта с
    идентификатором ID_VIEW_TIMER в классе CMainFrame. Назовите функцию OnViewTimer().


    Рис.5. Добавление функции OnViewTimer()

  • Добавив обработчик, щелкните кнопку Edit Code чтобы открыть для редактирования функцию CMyAppApp::OnViewTimer().
    Поместите в тело функции следующий код:

    CTimerDialog aTDlg;
    aTDlg.m_settime = g_timer.GetTime();
    if (aTDlg. DoModal() == IDOK)
    {
      g_timer.SetTime(aTDlg.m_settime);
      // Only one timer running per instance 
      if(!g_timer.m_thread)
        g_timer.m_thread = AfxBeginThread(DoTimer, 0); 
    }
    


    Рис.6. Текст функции OnViewTimer()

  • В начале файла MainFrm.cpp рядом с другими операторами #include введите следующую строку:
      #include "TimerDialog.h"
    
  • Откомпилируйте и соберите приложение МуАрр. Проверьте работу таймера, выбрав в меню View команду Timer.
    Появится одноименное диалоговое окно, в котором элемент управления Date Time Picker показывает текущее время. Установите
    новое время, превышающее текущее, напримерЮ на одну минуту и нажмите кнопку OK.

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

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

   
Текст измененного приложения можно взять здесь (64,9 Кб).

   
На следующем шаге мы рассмотрим создание потока пользовательского интерфейса.



Вы можете оставить комментарий, или Трекбэк с вашего сайта.

Оставить комментарий