Создание классов с потокамиАвтор: Покрашенко Александр. Встречая в форумах вопросы о том, как создавать классы с потоками, я решил написать эту статью. Вся проблема состоит в том, что для создания потока, необходимо передать адрес функции потока, а передать адрес функции, которая является членом класса нельзя. Можно завести статическую функцию, но тогда теряется смысл создавать классы, так как не получится использовать все возможности ООП при таком подходе. Конечно можно использовать класс MFC, но не все приложения используют эту библиотеку. Вся идея состоит в том, что мы все-таки опишем глобальную функцию, которую и будем передавать в качестве параметра функции CreateThread, а в свою очередь эта функция запустит на выполнение функцию потока в Вашем классе. Итак, создадим базовый класс: class CMyThread public: protected: DWORD m_dwID; Мы описали класс со всеми необходимыми функциями: запуска потока, остановки. Теперь напишем глобальную потоковую функцию: extern DWORD WINAPI ThreadProc(LPVOID lpParameter) Ключевое слово extern предотвратит использование этой функции вне этого файла программы. В качестве параметра получим указатель на объект, в котором и будет помещен код необходимый для выполнения в отдельном потоке. Хотя мы и преобразуем указатель к указателю на базовый класс, но вызовется функция наследующего класса. CMyThread::CMyThread() CMyThread::~CMyThread() Теперь перейдем к самому главному - запуску нашего потока: BOOL CMyThread::Execute() return (m_hThread==NULL)?FALSE:TRUE; } В качестве параметров мы передали адрес глобальной функции и указатель на наш объект! Эта функция завершает работу потока. Сперва она предупреждает поток о завершении, но если он сам не завершается, "убьем" его. DWORD CMyThread::Terminate(BOOL bCritical) m_bTerminated=TRUE; if (!bCritical)
GetExitCodeThread(m_hThread,&dwExitCode); Вот и все, базовый класс готов. Теперь создаем наш класс, с необходимыми характеристиками, функцией потока и наследуем базовый класс: class CMathThread : public CMyThread protected: CMathThread::CMathThread()
Вот эта функция и запустится на выполнение после вызова CMathThread::Execute() : DWORD CMathThread::ThreadFunc()
m_dValue+=.00001; // собственно вчисления
Создаем любые классы, переопределяем виртуальную функцию ThreadFunc() и создаем хоть сотню объектов такого типа, передавая им аргументы через члены класса. Отметим, что создать базовый класс нельзя, т.к. он содержит чисто виртуальную функцию, которую необходимо определять в наследующих классах. Опытные программисты сразу обнаружат несколько ошибок, которые никак нельзя допускать при работе с потоками. Например, неправильно использовать переменную для предупреждения потока о завершении (m_bTerminate). Второе, необходимо дописать механизмы общения с объектом с временной остановкой потока. Но целью статьи было объяснение создания подобных классов, потому что синхронизация потоков слишком большая тема для описания в данной статье. В данном случае я допустил использование переменной для предупреждения потока, т.к. она подразумевает только 2 состояния 0 и не 0. Но считывание переменной m_dValue при работающем потоке чревато. Необходимо использовать Event, Mutex, Semaphore либо другие механизмы для синхронизации с потоком для считывания общих данных. А это, как я уже сказал, совсем другая история… Примеры: Исполняемый - 5Кб Покрашенко Александр
|