// .hpp
typedef class CGenericVirtualClass
{
public:
virtual ~CGenericVirtualClass();
} *PGenericVirtualClass, *LPGenericVirtualClass;
// .cpp
CGenericVirtualClass::~CGenericVirtualClass() {};
Создание потоков в пределах экземпляра класса.
Перво-наперво, определяю такой класс:
Код:
И все свои основные рабочие классы в будущем я наследую от него. Что это даёт?:
1. Все наследуемые классы имеют таблицу виртуальных методов, с первой записью деструктора. Если где-то я буду подменять один объект на другой, я уверен, что при освобождении вызовется правильный деструктор;
2. Это необходимо для определения типа метода-члена класса. Делаю это:
Код:
// .hpp
typedef void (CGenericVirtualClass::*PGenericThreadProc) (void *pOption, DWORD & rRetResult);
typedef void (CGenericVirtualClass::*LPGenericThreadProc)(void *pOption, DWORD & rRetResult);
typedef void (CGenericVirtualClass::*PGenericThreadProc) (void *pOption, DWORD & rRetResult);
typedef void (CGenericVirtualClass::*LPGenericThreadProc)(void *pOption, DWORD & rRetResult);
В будущем любой класс, в котором мне нужно создать поток, я просто наследую от CGenericVirtualClass, и прописываю в нём метод-поток
void мойкласс::имяметода(void *, DWORD &);
Теперь нужно произвести его запуск. Для этого пишу функцию, и несколько её помощников:
Код:
// .hpp
// Макрос для удобства написания коды вызова
#define GENERIC_THREAD(x) ((LPGenericThreadProc)&x)
// Прототип функции создания потока
bool CreateGenericThread(
LPGenericVirtualClass pObject, // Обязательный указатель на объект
LPGenericThreadProc pEntryPoint, // Обязательный указатель на его метод
void *pOption = NULL, // Опциональные аргументы
LPHANDLE pRetThreadHandle = NULL); // Возвращаемый HANDLE потока
// .cpp
// Структура, передаваемая во внутренностях моего запускателя :-)
typedef struct SGenericVirtualStarterInfo
{
LPGenericVirtualClass pObject; // Объект, для которого запускаем метод
LPGenericThreadProc pThread; // Указатель на точку входа (метод)
void *pOption; // Опциональные аргументы потоку
} *PGenericVirtualStarterInfo, *LPGenericVirtualStarterInfo;
// Внутренности моего запускателя :-)
static DWORD WINAPI GenericVirtualThreadStarterFn(LPGenericVirtualStarterInfo pInfo)
{
// Возвращаемое потоком значение
DWORD Result = 0;
if(pInfo)
{
// Дубликат в стек, исключающий утечку памяти при использовании ExitThread(...);
SGenericVirtualStarterInfo Info = * pInfo;
delete pInfo;
// Вызов метода в экземпляре класса
if(Info.pObject && Info.pThread)
(Info.pObject->*(Info.pThread))(Info.pOption, Result);
};
return Result;
};
// Сама функция запуска. Вернет true, если поток удачно создан, иначе false.
bool CreateGenericThread(
LPGenericVirtualClass pObject, LPGenericThreadProc pEntryPoint,
void *pOption, LPHANDLE pRetThreadHandle)
{
// Если я не получаю указатель на объект и указатель на метод, выйти
if(!(pObject && pEntryPoint))
return false;
// Создаю временный блок с данными для передачи новому потоку
LPGenericVirtualStarterInfo pInfo = new SGenericVirtualStarterInfo;
if(pInfo == NULL)
return false;
// Заполняю поля этого блока
pInfo->pObject = pObject;
pInfo->pThread = pEntryPoint;
pInfo->pOption = pOption;
// Пытаюсь создать новый поток
HANDLE hThread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)&GenericVirtualThreadStarterFn,
(LPVOID)pInfo, 0, NULL);
// Если извне просят HANDLE, верну его
if(pRetThreadHandle)
*pRetThreadHandle = hThread;
// Если поток удачно создан, возвращаю true
if(hThread)
return true;
// Иначе долой выделенную память и верну false
delete pInfo;
return false;
};
// Макрос для удобства написания коды вызова
#define GENERIC_THREAD(x) ((LPGenericThreadProc)&x)
// Прототип функции создания потока
bool CreateGenericThread(
LPGenericVirtualClass pObject, // Обязательный указатель на объект
LPGenericThreadProc pEntryPoint, // Обязательный указатель на его метод
void *pOption = NULL, // Опциональные аргументы
LPHANDLE pRetThreadHandle = NULL); // Возвращаемый HANDLE потока
// .cpp
// Структура, передаваемая во внутренностях моего запускателя :-)
typedef struct SGenericVirtualStarterInfo
{
LPGenericVirtualClass pObject; // Объект, для которого запускаем метод
LPGenericThreadProc pThread; // Указатель на точку входа (метод)
void *pOption; // Опциональные аргументы потоку
} *PGenericVirtualStarterInfo, *LPGenericVirtualStarterInfo;
// Внутренности моего запускателя :-)
static DWORD WINAPI GenericVirtualThreadStarterFn(LPGenericVirtualStarterInfo pInfo)
{
// Возвращаемое потоком значение
DWORD Result = 0;
if(pInfo)
{
// Дубликат в стек, исключающий утечку памяти при использовании ExitThread(...);
SGenericVirtualStarterInfo Info = * pInfo;
delete pInfo;
// Вызов метода в экземпляре класса
if(Info.pObject && Info.pThread)
(Info.pObject->*(Info.pThread))(Info.pOption, Result);
};
return Result;
};
// Сама функция запуска. Вернет true, если поток удачно создан, иначе false.
bool CreateGenericThread(
LPGenericVirtualClass pObject, LPGenericThreadProc pEntryPoint,
void *pOption, LPHANDLE pRetThreadHandle)
{
// Если я не получаю указатель на объект и указатель на метод, выйти
if(!(pObject && pEntryPoint))
return false;
// Создаю временный блок с данными для передачи новому потоку
LPGenericVirtualStarterInfo pInfo = new SGenericVirtualStarterInfo;
if(pInfo == NULL)
return false;
// Заполняю поля этого блока
pInfo->pObject = pObject;
pInfo->pThread = pEntryPoint;
pInfo->pOption = pOption;
// Пытаюсь создать новый поток
HANDLE hThread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)&GenericVirtualThreadStarterFn,
(LPVOID)pInfo, 0, NULL);
// Если извне просят HANDLE, верну его
if(pRetThreadHandle)
*pRetThreadHandle = hThread;
// Если поток удачно создан, возвращаю true
if(hThread)
return true;
// Иначе долой выделенную память и верну false
delete pInfo;
return false;
};
Всё готово.
Далее, показываю на примере, как использовать написанный код. Дополнительно в этом примере показывается, как дожидаться завершения потока в деструкторе.
Код:
// .hpp
class CMyDemo : public CGenericVirtualClass
{
protected:
struct
{
HANDLE hThread;
bool bThreadWork;
} Sync;
void ThreadFn(void * pOption, DWORD & rRetResult);
public:
CMyDemo();
virtual ~CMyDemo();
};
// .cpp
CMyDemo::CMyDemo() // Конструктор
{
Sync.hThread = NULL;
Sync.bThreadWork = true;
CreateGenericThread(this, GENERIC_THREAD(CMyDemo::ThreadFn), NULL, &Sync.hThread);
};
CMyDemo::~CMyDemo() // Деструктор
{
// Подать какой-то сигнал в поток, что пора завершаться. В моём (!) примере это будет так:
Sync.bThreadWork = false;
// Ожидаю завершения
if(Sync.hThread)
{
WaitForSingleObject(Sync.hThread, INFINITE);
CloseHandle(Sync.hThread);
Sync.hThread = NULL;
};
};
void CMyDemo::ThreadFn(void * pOption, DWORD & rRetResult) // Поток в классе
{
while(Sync.bThreadWork)
{
Sleep(1000);
MessageBeep(MB_OK);
};
};
// .cpp
int main() // Пример приложения, использующего демонстрационный класс
{
// Создаю объект
CMyDemo Demo;
// Ничего не делаю 5 секунд
Sleep(5000);
// Удачи!
return 0;
};
class CMyDemo : public CGenericVirtualClass
{
protected:
struct
{
HANDLE hThread;
bool bThreadWork;
} Sync;
void ThreadFn(void * pOption, DWORD & rRetResult);
public:
CMyDemo();
virtual ~CMyDemo();
};
// .cpp
CMyDemo::CMyDemo() // Конструктор
{
Sync.hThread = NULL;
Sync.bThreadWork = true;
CreateGenericThread(this, GENERIC_THREAD(CMyDemo::ThreadFn), NULL, &Sync.hThread);
};
CMyDemo::~CMyDemo() // Деструктор
{
// Подать какой-то сигнал в поток, что пора завершаться. В моём (!) примере это будет так:
Sync.bThreadWork = false;
// Ожидаю завершения
if(Sync.hThread)
{
WaitForSingleObject(Sync.hThread, INFINITE);
CloseHandle(Sync.hThread);
Sync.hThread = NULL;
};
};
void CMyDemo::ThreadFn(void * pOption, DWORD & rRetResult) // Поток в классе
{
while(Sync.bThreadWork)
{
Sleep(1000);
MessageBeep(MB_OK);
};
};
// .cpp
int main() // Пример приложения, использующего демонстрационный класс
{
// Создаю объект
CMyDemo Demo;
// Ничего не делаю 5 секунд
Sleep(5000);
// Удачи!
return 0;
};
Отдельно стоит заметить, что поток вовсе не обязательно создавать в конструкторе и удалять в деструкторе, и количество потоков также может быть произвольным.
Не бойтесь использовать своё воображение, и про логику/адекватность не забывайте, конечно.
Надеюсь, что это будет кому-нибудь полезно. Спасибо за внимание.