Иногда функциональности глобальной функции или статического метода класса не хватает при запуске нового потока. С методом класса новый поток не создашь. Компилятор выдаст вот такую ошибку: error C2664: 'CreateThread' : cannot convert parameter 3 from 'unsigned long (__thiscall CMyClass::*)(void *)' to 'unsigned long (__stdcall *)(void *)' Функцию __thiscall просто так к __stdcall не приведешь, ведь для метода передается еще указатель на класс. Но если немного схитрить, то это можно обойти. Вместо метода мы создадим поток со статической функцией. А в качестве данных, передадим указатель на структуру, в которую положим: 1. Указатель на экземпляр класса. 2. Указатель на метод класса. 3. Указатель на дествительные данные для метода. Когда новый поток запустится, то статическая функция, зная указатель на класс и нужный метод, запустит этот метод в новом потоке. class CMyClass; typedef DWORD (CMyClass::*LPTHREAD_METHOD)(LPVOID pParam);
// Структура параметров для статической функции. typedef struct STARTUP_PARAM { CMyClass* pClass; LPTHREAD_METHOD pMethod; LPVOID pParam; } *LPSTARTUP_PARAM;
// Объявление класса. class CMyClass { public: // Метод, который мы хотели бы запустить в новом потоке. DWORD Process (LPVOID pParam);
// Функция, которая создает новый поток. HANDLE StartThread (LPTHREAD_METHOD pMethod, LPVOID pParam, LPDWORD pdwThreadID = NULL, LPSECURITY_ATTRIBUTES pSecurity = NULL , DWORD dwStackSize = 0 , DWORD dwFlags = 0); protected: // Статическая функция, которая запустит метод. static DWORD StartFunc (LPSTARTUP_PARAM pStartup); };
// В этом методе можно реализовать вычисления, которые вам нужны. DWORD CMyClass::Process(LPVOID pParam) { Sleep(1000); return 0; }
HANDLE CMyClass::StartThread(LPTHREAD_METHOD pMethod, LPVOID pParam, LPDWORD pdwThreadID /* = NULL */, LPSECURITY_ATTRIBUTES pSecurity /* = NULL */, DWORD dwStackSize /* = 0 */, DWORD dwFlags /* = 0 */) { // Создаем структуру и упаковываем данные для статической функции. LPSTARTUP_PARAM pStartup = new STARTUP_PARAM; pStartup->pClass = this; pStartup->pMethod = pMethod; pStartup->pParam = pParam;
// Создаем новый поток. return CreateThread(pSecurity, dwStackSize, (LPTHREAD_START_ROUTINE)StartFunc, pStartup, dwFlags, pdwThreadID); }
// В новом потоке вначале вызывается функция CMyClass::StartFunc(...) // А она запускает наш метод. DWORD CMyClass::StartFunc(LPSTARTUP_PARAM pStartup) { // Распаковываем данные в новом потоке. // Получаем указатель на класс и на метод класса. CMyClass* pClass = pStartup->pClass; LPTHREAD_METHOD pMethod = pStartup->pMethod; LPVOID pParam = pStartup->pParam;
// Запускаем метод класса в новом потоке. DWORD dwResult = (pClass->*pMethod)(pParam);
// Удаляем временные данные и возвращаем код возврата из нового потока. delete pStartup; return dwResult; }
Подробнее остановимся на функции CMyClasss::StartThread Функция создает новый поток в виртуальном адресном пространстве вызывающего процесса.
HANDLE CMyClass::StartThread( LPTHREAD_METHOD pMethod, LPVOID pParam, LPDWORD pdwThreadID /* = NULL */, LPSECURITY_ATTRIBUTES pSecurity /* = NULL */, DWORD dwStackSize /* = 0 */, DWORD dwFlags /* = 0 */ ); Эта функция аналогична функции Win32 API HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ); Параметры: pMethod [in] указатель на метод класса, для вызова в новом потоке. pParam [in] указатель на данные для нового потока. pdwThreadID [out] указатель на переменную, которая получит идентификатор потока. pSecurity [in] указатель на структуру SECURITY_ATTRIBUTES, которая устанавливает, может ли дочерний процесс унаследовать возвращаемый хендл потока. Если pSecurity равно NULL, хендл не может быть унаследован. Windows NT/2000/XP: Поле lpSecurityDescriptor структуры, устанавливает атрибуты безопастности для нового потока. Если pSecurity равно NULL, потоку даются атрибуты по умолчанию. dwStackSize [in] Изначальный размер стека, в байтах. Система округлит это значение до ближайшей страницы. Если значение равно нулю, новый поток возьмет размер по умолчанию. dwCreationFlags [in] Флаги для создания потока. Если установлен флаг CREATE_SUSPENDED, поток создается в приостановленном состоянии и не будет запущен, пока не будет вызвана фугкция ResumeThread. Если значение равно нулю, то поток запустится сразу после создания. Результат: В случае успеха, возвратится хендл созданного потока. В случае неудачи, возвратится NULL.
Пример:CMyClass MyClass;
char* pStr = new char [6]; strcpy(pStr, "Hello");
DWORD dwThreadID; HANDLE hThread = MyClass.StartThread(CMyClass::Process, (LPVOID)pStr, &dwThreadID); Метод CMyClass::Process, запустится в новом потоке и значение this будет равно указателю на класс MyClass.
Примечание: При работе с классами в разных потоках не забывайте о синхронизации. |