На этом шаге мы рассмотрим алгоритм создания рабочего потока.
Для рабочего потока необходимо написать код управляющей функции, предназначенной для реализации основной задачи потока. Ее адрес передается в качестве параметра функции 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 на активный поток.
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
CTimer g_timer;
Далее создадим функцию рабочего потока, которая с интервалом в одну секунду будет сравнивать системное время и время таймера. При их совпадении функция выведет информационное окно и переустановит таймер.
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;
}
Рис.2. Добавление функции DoTimer()
Рис.3. Создание диалогового окна
Рис.4. Добавление переменной m_settime
Рис.5. Добавление функции 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()
#include "TimerDialog.h"
Во время ожидания таймера поэкспериментируйте с другими функциями интерфейса МуАрр. Вы не заметите снижения их производительности за счет работы независимого рабочего потока, каждую секунду проверяющего системное время.
Когда системные часы покажут время, установленное в диалоговом окне Timer, появится информационное окно и рабочий поток завершится.
Текст измененного приложения можно взять здесь (64,9 Кб).
На следующем шаге мы рассмотрим создание потока пользовательского интерфейса.