Передача этого указателя в CreateThread в конструкторе класса ведет себя странно в процедуре потока
В конструкторе одного из моих классов я вызываю функцию Windows CreateThread
как последняя операция. Поток создан для немедленного выполнения, и я передаю указатель this моего класса как lpParameter
,
В процедуре потока я приведу параметр, переданный обратно к указателю моего класса, и назову его pThis
,
я могу увидеть это pThis
указывает на ту же область памяти, что и this
указатель я передал, когда я позвонил CreateThread
, Однако, если я посмотрю на переменные-члены, к которым обращается pThis->...
все они имеют неправильные значения.
Я ожидал ценность this->member_variable
используется в классе, к которому принадлежит указатель this, и совпадает с указателем, полученным при записи pThis->member_variable
в процедуре потока.
Если я позвоню CreateThread
в другой функции-члене (не в конструкторе) все ведет себя правильно.
Отсюда вопрос: запрещено ли вызывать функцию Windows CreateThread
из конструктора класса C++? Если да, в чем проблема?
Разъяснение:
1) Я могу подтвердить, что объект всегда существует. Объект выходит из области видимости только после завершения всей программы. Как я уже сказал: звоню CreateThread
от какой-то другой функции-члена работает.
2) Исправлена "проволочная" опечатка, должна быть "странная", извините.
Некоторый код:
Я стараюсь размещать фрагменты кода, сводя вещи к минимуму, сохраняя при этом "неисправные" части.
class CTimerW32 : public CTimer
{
public:
CTimerW32();
~CTimerW32();
private:
static void CALLBACK TimerCallback(LPVOID lpParam, BOOLEAN bReason);
static DWORD WINAPI WaitCompletition(LPVOID lpParam);
private:
HANDLE m_hCompletitionEvent;
HANDLE m_hCompletitionThread;
bool m_bStartDeferred;
};
Вы можете смело игнорировать базовый класс CTimer
так как это просто абстрактный базовый класс, позволяющий строить на разных платформах.
CTimerW32::CTimerW32()
{
m_bStartDeferred= false;
m_hCompletitionEvent= CreateEvent(NULL, FALSE, FALSE, NULL);
m_hCompletitionThread= CreateThread(NULL, 0, WaitCompletition, this, 0, NULL);
}
Здесь я вижу, что m_hCompletitionEvent
действует после звонка CreateEvent
,
DWORD WINAPI CTimerW32::WaitCompletition(LPVOID lpParam)
{
CTimerW32* pThis;
DWORD dwRet;
pThis= (CTimerW32*)(lpParam);
while (true) {
// just wait for the completition event to be signaled
dwRet= WaitForSingleObject(pThis->m_hCompletitionEvent, INFINITE);
// ...
if (pThis->m_bStartDeferred) {
// ...
}
}
Здесь дела идут не так, как надо WaitForSingleObject
, Как уже говорилось, указатель this объекта класса CTimerW32
(сейчас pThis
) все еще имеет то же значение, что и указатель this при создании потока. Однако ручка в pThis->m_hCompletitionEvent
похоже на случайные данные. Это не значение, наблюдаемое после вызова CreateEvent
в конструкторе.
2 ответа
Создание потока в конструкторе не должно быть проблемой. Кроме того, ваш объект должен быть полностью инициализирован списком инициализаторов, прежде чем какой-либо код в конструкторе будет запущен для создания потока, поэтому инициализация, вероятно, не является проблемой.
Вероятно, объект, который вы наблюдаете, находится вне области видимости, и его деструктор вызывается до того, как вы заметите его в новом потоке. Попробуйте создать объект динамически с новым и посмотреть, если это все еще происходит, я уверен, что это не так, поскольку объект не будет уничтожен, когда он выходит из области видимости.
Очевидно, вы должны держать указатель на этот объект в более широкой области видимости, чтобы вы могли в конечном итоге удалить его тоже:)
Вам, вероятно, удастся отладить эту проблему с помощью Application Verifier. Если вы включите опцию "Основы" для своей программы, она включит PageHeap, который сразу же выйдет из строя, когда освободится память. Если вы распределяете стек по переменной таймера, вам не повезло, но в отладчике должно быть возможно увидеть, если в момент, когда вы заметите повреждение, поток, создавший таймер, все еще находится внутри функции, где Функция CTimerW32 была объявлена.
Наконец, для этого варианта использования API-интерфейсы Threadpool Timer могут работать проще и с меньшими затратами ресурсов, чем создание собственного выделенного потока.