На этом шаге мы рассмотрим создание потока пользовательского интерфейса.
Как мы уже говорили ранее, все потоки MFC-приложения представлены объектами класса CWinThread и создаются функцией AfxBeginThread(). При организации рабочего потока она создает объект и присваивает переменной класса WinThread::m_pfnThreadProc адрес функции потока. Для потока пользовательского интерфейса нужно создать класс, производный от CWinThread, и передать его информацию времени выполнения в соответствующую версию AfxBeginThread().
Во фрагменте кода, взятом из исходных текстов MFC, показано, как каркас различает рабочий поток и поток пользовательского интерфейса. Этот код находится в функции _AfxThreadEntry() - входной точке для всех MFC-потоков.
// Сначала - проверка на простой рабочий поток DWORD nResult = 0; if (pThread->m_pfnThreadProc != NULL) { nResult = (*pThread->m_pfnThreadProc)(pThread ->m_pThreadParams); ASSERT_VALID(pThread); } // Иначе - проверка на поток с циклом обработки сообщений else if (!pThread->InitInstance()) { ASSERT_VALID(pTh read); nResult = pThread->ExitInstance(); } else { // Остановка после вызова PostQuitMessage ASSERT_VALID(pThread); nResult = pThread->Run(); } // Очистить и закрыть поток threadWnd.Detach(); AfxEndThread(nResult);
Если m_pfnThreadProc указывает на управляющую функцию, это - рабочий поток. В таком случае для завершения потока вызывается его управляющая функция. Если же значение m_pfnThreadProc равно NULL, то речь идет о потоке пользовательского интерфейса. Тогда для инициализации потока вызывается функция-член InitInstance(), которая создает главное окно и другие объекты, в том числе элементы пользовательского интерфейса. Если Initlnstance() завершается успешно (возвращая TRUE), то вызывается функция Run(). В CWinThread::Run() реализован цикл обработки сообщений, направляемых в главное окно потока.
Функции Initlnstance() и Run() Вам наверняка уже знакомы, так как представляют собой ключевые функции, наследуемые от CWinApp.
Вам необходимо перегрузить функцию InitInstance() для своего класса потока. Часто Вам придется перегружать и функцию CWinThread::ExitInstance() для правильной очистки потока перед завершением. Что касается функции Run(), то в основном Вы будете вызывать версию базового класса.
В следующем примере представлен простейший способ создания класса, производного от CWinThread.
Рис.1. Создание класса CMyUIThread
Откройте файлы MyUIThread.h и MyUIThread.cpp и изучите исходный код класса потока. Обратите внимание, ClassWizard поместил заглушки вместо функций InitInstance() и ExitInstance(), и Вам предстоит добавить в них свой код. Кроме того, заметьте, что в классе уже имеется карта сообщений. Макросы DECLARE_DYNCREATE и IMPLEMENT_DYNCREATE играют очень важную роль, так как при вьшолнении кода они передают производным от CObject классам информацию, которая требуется варианту функции AfxBeginThread() для пользовательского интерфейса.
Завершив редактирование класса потока, вызовите версию AfxBeginThread() для пользовательского интерфейса, передав ей указатель на информацию времени выполнения о классе потока:
AfxBeginThread(RUNTIME_CLASS(CMyUIThread));
Макрос RUNTIME_CLASS возвращает указатель на структуру CRuntimeClass, которая поддерживает информацию времени выполнения для всех производных от CObject классов, объявленных макросами DECLARE_DYNAMIC, DECLARE_DYNCREATE и DECLARE_SERIAL. Это позволяет AfxBeginThread() создать объект потока правильного типа.
Версия AfxBeginThread(), создающая поток пользовательского интерфейса, имеет тот же набор параметров по умолчанию, что и версия для рабочих потоков, и также возвращает указатель на объект созданного потока.
Текст измененного приложения можно взять здесь (64,7 Кб).
На следующем шаге мы рассмотрим синхронизацию потоков.