У нас есть следующее определение класса:
CThread::CThread ()
{
this->hThread = NULL;
this->hThreadId = 0;
this->hMainThread = ::GetCurrentThread ();
this->hMainThreadId = ::GetCurrentThreadId ();
this->Timeout = 2000; //milliseconds
}
CThread::~CThread ()
{
//waiting for the thread to terminate
if (this->hThread) {
if (::WaitForSingleObject (this->hThread, this->Timeout) == WAIT_TIMEOUT)
::TerminateThread (this->hThread, 1);
::CloseHandle (this->hThread);
}
}
//*********************************************************
//working method
//*********************************************************
unsigned long CThread::Process (void* parameter)
{
//a mechanism for terminating thread should be implemented
//not allowing the method to be run from the main thread
if (::GetCurrentThreadId () == this->hMainThreadId)
return 0;
else {
m_pMyPointer = new MyClass(...);
// my class successfully works here in another thread
return 0;
}
}
//*********************************************************
//creates the thread
//*********************************************************
bool CThread::CreateThread ()
{
if (!this->IsCreated ()) {
param* this_param = new param;
this_param->pThread = this;
this->hThread = ::CreateThread (NULL, 0, (unsigned long (__stdcall *)(void *))this->runProcess, (void *)(this_param), 0, &this->hThreadId);
return this->hThread ? true : false;
}
return false;
}
//*********************************************************
//creates the thread
//*********************************************************
int CThread::runProcess (void* Param)
{
CThread* thread;
thread = (CThread*)((param*)Param)->pThread;
delete ((param*)Param);
return thread->Process (0);
}
MyClass* CThread::getMyPointer() {
return m_pMyPointer;
}
В основной программе у нас есть следующее:
void main(void) {
CThread thread;
thread.CreateThread();
MyClass* myPointer = thread.getMyPointer();
myPointer->someMethod(); // CRASH, BOOM, BANG!!!!
}
В момент использования myPointer (в основном потоке) происходит сбой. Я не знаю, как получить указатель, указывающий на память, выделенную в другом потоке. На самом ли деле это возможно?





Пространство памяти для вашего приложения доступно для всех потоков. По умолчанию любая переменная видна любому потоку независимо от контекста (единственное исключение - переменные, объявленные __delcspec (thread))
Вы получаете сбой из-за состояния гонки. Только что созданный поток еще не запущен в момент, когда вы вызываете getMyPointer. Вам нужно добавить какую-то синхронизацию между вновь созданным потоком и исходным потоком. Другими словами, исходный поток должен ждать, пока новый поток не сообщит ему, что он создал объект.
Как заметил Роб Уокер, я очень скучал по гоночным условиям. Также сбой не при получении указателя, а при его использовании.
Простое ожидание сделало свою работу:
MyClass* myPointer = thread.getMyPointer();
while (myPointer == 0)
{
::Sleep(1000);
}
myPointer->someMethod(); // Working :)
Хотя добавление сна, похоже, решит проблему, это все еще скрытая ошибка. Нет никакой гарантии, что поток будет запущен и перейдет в нужное место в течение 1 секунды. Единственная гарантия потребует явного сигнала между потоками.
Фактически, в этом конкретном случае вы вращаете мог (с небольшим засыпанием), ожидая, что возвращаемый указатель будет ненулевым. В целом не рекомендуемый шаблон, но он сделает его безопаснее и в целом быстрее, чем явный сон.
Вместо этого использование условной переменной будет быстрее. Кроме того, ваш добавленный код даже не работает, вам нужно вызвать getMyPointer в цикле. Даже тогда, без блокировки мьютекса или volatile на m_pMyPointer нет гарантии, что он будет работать.
Я пытаюсь понять, что вы пытаетесь сделать. Это выглядит слишком сложно для чего-то вроде класса потока. Не могли бы вы также опубликовать определение класса?
Для начала удалите приведение аргумента процесса к CreateThread () в стиле C:
this->hThread = ::CreateThread (NULL, 0,&runProcess, (void *)(this_param), 0, &this->hThreadId);
Если это не компилируется, вы делаете что-то не так! Никогда никогда приводит указатель на функцию! Если компилятор жалуется, что вам нужно изменить функцию, не пытайтесь избавиться от ошибок! Действительно! Вы только сделаете себе хуже! Если вы сделаете это снова, Oни * придет к вам домой и сделает ... Посмотрим, как вам это понравится! Серьезно, не делай этого снова.
Кстати, в Process () я думаю, что было бы правильнее сделать что-то вроде:
assert(::GetCurrentThreadId() == hThreadId);
Но если вы объявите его частным, он должен быть доступен только вашему классу CThread, и поэтому это не должно быть проблемой. Хотя утверждения хороши!
* Непонятно, кто такие Oни, но понятно, что бы Oни ни делал, это не будет приятно!
Вы абсолютно правы, и я абсолютно сплю :) Большое спасибо! Я отправлю исправленный код в качестве ответа ниже.