Исполнение функции-члена класса в потоке.
От: KickingBear  
Дата: 27.08.11 05:30
Оценка:
Здравствуйте.

Есть академический интерес в создании небольшого набора классов-оберток вокруг апишных функций работы с потоками и объектам синхронизации. ATL хорошо, но громоздко.
Сейчас никак не соображу, как правильно написать код, позволяющий в качестве рабочей функции потока указать член-функцию класса. Делаю так:


template<class T, DWORD(T::*ClassFunc)()>
inline DWORD WINAPI thread_to_member_thunk(void* object)
{
    return (reinterpret_cast<T*>(object)->*ClassFunc)();
}

template<class T>
void ThreadClass::Start(T *object, DWORD(T::*ClassFunction)())
{
    CreateThread(0, 0, thread_to_member_thunk<T, this->*ClassFunction>, object, 0, 0);
};

и вызываем функцию:
m_Thread.Start(this, &MyClass::MainThreadFunction);


Компилятор ругается:
error C2440: 'type cast' : cannot convert from 'DWORD (__stdcall *)(void *)' to 'LPTHREAD_START_ROUTINE'

Не понимаю почему.
Так же камрады посоветовали пойти обычным путем:


class Test
{
public:
    DWORD ThreadFunc(){return 0;};

    template<class T>
    void StartThread(T *object, DWORD(T::*ClassFunc)())
    {
        CreateThread(0, 0, ClassFunc, object, 0, 0);
    }
};

class Test2
{
public:
//     Test2()
//     {
//         CreateThread(0, 0, &Test2::ThreadFunc, this, 0, 0);
//     }

    static DWORD WINAPI ThreadFunc(LPVOID param)
    {
        Test2 *localObj = reinterpret_cast<Test2*>(param);
        return localObj->ThreadFunc();
    };

    DWORD ThreadFunc()
    {
        return 0;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Test  myThr;
    Test2 myObj;

    myThr.StartThread(&myObj, &Test2::ThreadFunc);

    return 0;
}


Спокойно компилируется, если создавать поток в конструкторе Test2 (закоменченный код). Но мне хочется инкапсулировать все эти хэндлы в отдельном классе, потому пытаемся создать поток через класс Test. Получаем точно такую же ошибку.
Буду благодарен советам.
Re: Исполнение функции-члена класса в потоке.
От: Слава Израиль  
Дата: 27.08.11 07:18
Оценка:
Здравствуйте, KickingBear, Вы писали:

KB>Здравствуйте.


Функция потока должна быть статической.
Спасибо за внимание
Re: Исполнение функции-члена класса в потоке.
От: Masterkent  
Дата: 27.08.11 07:30
Оценка: 1 (1)
KickingBear:

KB> CreateThread(0, 0, thread_to_member_thunk<T, this->*ClassFunction>, object, 0, 0);


1. Вместо CreateThread следует использовать _beginthreadex (см. описание CreateThread в MSDN).

2. thread_to_member_thunk<T, this->*ClassFunction> — это какая-то бессмыслица. Нельзя передавать шаблонным аргументом первое что под руку попало. Если ты хочешь, чтобы указатель на нестатический метод мог вычисляться во время выполнения программы, то тебе нужно передавать его не шаблонным аргументом в некую функцию-переходник, а через параметр типа void* функции _beginthreadex (т.е. тем же путём, что и объект). Вдобавок тебе следует обдумать, кто должен владеть объектом. Если твой объект скончается до того, как создаваемый поток завершит выполнение метода этого объекта, то может случиться что-то нехорошее.
Re[2]: Исполнение функции-члена класса в потоке.
От: KickingBear  
Дата: 27.08.11 07:51
Оценка:
Здравствуйте, Masterkent, Вы писали:

M>KickingBear:

M>thread_to_member_thunk<T, this->*ClassFunction> — это какая-то бессмыслица.

Да, оплошал. По-видимому, какой-то невалидный эксперимент скопировал в форум, вместо нормального кода)
По поводу beginthreadex — уже не в первый раз натыкаюсь, почитаю.
Спасибо за совет
Re[3]: Исполнение функции-члена класса в потоке.
От: jyuyjiyuijyu  
Дата: 27.08.11 08:12
Оценка:
Здравствуйте, KickingBear, Вы писали:

извращаются даже так
#include <windows.h> 

#pragma pack(push,1) 
struct _ProcThunk 
{ 
   DWORD   m_mov;          
   DWORD   m_this;          
   BYTE    m_jmp;          
   DWORD   m_relproc;      
}; 
#pragma pack(pop) 

class ProcThunk 
{ 
public: 
   union 
   { 
      _ProcThunk thunk; 
   }; 
   void Init(void* proc, void* pThis) 
   { 
      thunk.m_mov = 0x042444C7; 
      thunk.m_this = (DWORD)pThis; 
      thunk.m_jmp = 0xe9; 
      thunk.m_relproc = (int)proc - ((int)this+sizeof(_ProcThunk)); 
      FlushInstructionCache(GetCurrentProcess(), &thunk, sizeof(thunk)); 
   } 
}; 

__declspec (naked) void* _cdecl pvoid_cast(...) 
{ 
   _asm 
   { 
      mov eax, [esp+4] 
      ret 
   } 
} 

class Thread 
{ 
   ProcThunk thunk_; 
public: 
   DWORD Worker(void* p) 
   { 
      ::MessageBox("Thread", "Thread", 0, 0); 
      return 0; 
   } 

   Thread() 
   { 
      thunk_.Init(pvoid_cast(Worker), (void*)this); 
   } 
    
   operator LPTHREAD_START_ROUTINE() 
   { 
      return (LPTHREAD_START_ROUTINE)&(thunk_.thunk); 
   } 
}; 

int main(int argc, char* argv[]) 
{ 
   Thread t; 

   HANDLE hThread = CreateThread(NULL, 0, t, NULL, 0, 0 ); 
    
   WaitForSingleObject(hThread, INFINITE); 

   return 0; 
}
Re: Исполнение функции-члена класса в потоке.
От: jyuyjiyuijyu  
Дата: 27.08.11 08:19
Оценка:
у меня у одного даже желания никогда не возникает точкой входа в поток делать функцию класса ?
использую потоки как глобальные функции
Re: Исполнение функции-члена класса в потоке.
От: enji  
Дата: 27.08.11 10:16
Оценка: 5 (1)
Здравствуйте, KickingBear, Вы писали:

KB>Есть академический интерес в создании небольшого набора классов-оберток вокруг апишных функций работы с потоками и объектам синхронизации. ATL хорошо, но громоздко.

KB>Сейчас никак не соображу, как правильно написать код, позволяющий в качестве рабочей функции потока указать член-функцию класса. Делаю так:

Посмотрите на boost::thread.

Если делать его своими руками, то можно в качестве "хранилища" для произвольных функций использовать boost::function.

Если и boost::function не хотите использовать, то вот тут http://habrahabr.ru/blogs/cpp/78299/ объясняют, как сделать что-то похожее своими руками
Re: Исполнение функции-члена класса в потоке.
От: _nn_ www.nemerleweb.com
Дата: 28.08.11 07:57
Оценка:
Здравствуйте, KickingBear, Вы писали:

KB>Здравствуйте.


Да тут нечего мудрить:
template<typename T, unsigned (T::*Method)()> 
unsigned __stdcall ThreadThunk(void* arg)
{
  return (static_cast<T*>(arg)->*Method)();
}

class A
{
 public:
  A()
  {
     _beginthreadex(
    NULL,
    0,
    ThreadThunk<A, &A::run>,
    this,
    0,
    NULL)));
  }

private:
  unsigned run()
  {
    return 0;
  }
};


Никаких аллокаций
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re: Исполнение функции-члена класса в потоке.
От: alexey_sz  
Дата: 29.08.11 08:20
Оценка:
Здравствуйте, KickingBear, Вы писали:

boost thread + boost bind
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.