Метод класса в новом потоке
Иногда функциональности глобальной функции или статического метода класса не хватает при запуске нового потока. С методом класса новый поток не создашь. Компилятор выдаст вот такую ошибку:
1. Указатель на экземпляр класса.
2. Указатель на метод класса.
3. Указатель на дествительные данные для метода.
Когда новый поток запустится, то статическая функция, зная указатель на класс и нужный метод, запустит этот метод в новом потоке.
Функция создает новый поток в виртуальном адресном пространстве вызывающего процесса.
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.
Пример:
Примечание: При работе с классами в разных потоках не забывайте о синхронизации.
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;Подробнее остановимся на функции CMyClasss::StartThread
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;
}
Функция создает новый поток в виртуальном адресном пространстве вызывающего процесса.
HANDLE CMyClass::StartThread(Эта функция аналогична функции Win32 API
LPTHREAD_METHOD pMethod, LPVOID pParam,
LPDWORD pdwThreadID /* = NULL */,
LPSECURITY_ATTRIBUTES pSecurity /* = NULL */,
DWORD dwStackSize /* = 0 */,
DWORD dwFlags /* = 0 */
);
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;Метод CMyClass::Process, запустится в новом потоке и значение this будет равно указателю на класс MyClass.
char* pStr = new char [6];
strcpy(pStr, "Hello");
DWORD dwThreadID;
HANDLE hThread = MyClass.StartThread(CMyClass::Process, (LPVOID)pStr, &dwThreadID);
Примечание: При работе с классами в разных потоках не забывайте о синхронизации.