将指向成员函数的指针传递到单独的线程



我使用OpenGL来显示图形数据和自定义UI元素。我有一个选项卡式SDI,其中文档窗口使用无边界窗口作为视口,每个视口包含一个单独的线程,仅用于运行GL绘图操作,而所有其他操作(鼠标、键盘等(都应在视口回调中处理。图形线程是使用_beginthreadex创建的;

void ViewportController::create(){
hThreadHandle = reinterpret_cast<HANDLE>(
_beginthreadex(NULL,
0,
&threadProc,
(void*)lpThreadController,
0,
&uiThreadID)
);
if (hThreadHandle == 0)
{
throw std::exception("Failed to create thread");
}
}

start_address函数包含一个循环,该循环使用PeekMessage允许视口将消息传递给图形线程,ThreadController类作为arglist参数传递;

unsigned __stdcall ViewportController::threadProc(void* pArguments)
{
ThreadController* ctrl = static_cast<ThreadController*>(pArguments);
MSG mMsg = { 0 };
bool bStop = false;
if (wglMakeCurrent(ctrl->getDC(), ctrl->getRC()))
{
ctrl->InitGL();
if (!bStop)
bStop = (bool)!SendMessage(ctrl->getHandle(), UWM_SHOW, 0, 0);
}
while (!bStop)
{
while ((bool)PeekMessage(&mMsg, (HWND)(-1), 0, 0, PM_REMOVE))
{
switch (mMsg.message)
{
case UWM_DRAW:
ctrl->draw(mMsg.lParam, mMsg.lParam);
break;
case WM_CLOSE:
bStop = true;
break;
case WM_KEYDOWN:
ctrl->keyDown(mMsg.wParam, mMsg.lParam);
break;
case WM_LBUTTONDOWN:
ctrl->lButtonDown(mMsg.lParam, mMsg.lParam);
break;
case WM_MOUSEMOVE:
ctrl->mouseMove(mMsg.lParam, mMsg.lParam);
break;
case WM_MOUSEWHEEL:
ctrl->mouseWheel(mMsg.lParam, mMsg.lParam);
break;
case WM_PAINT:
ctrl->paint();
break;
case WM_SIZE:
ctrl->size(mMsg.lParam, mMsg.lParam);
break;
default:
return 0;
}
SwapBuffers(ctrl->getDC());
}
}
_endthreadex(0);
return 0;
}

目前,ThreadController类中包含了所有自定义UI元素(文本、编辑框等(。这使得UI的动态创建变得非常困难,更重要的是,它违背了只有图形管道的目标,所以我想做的是这样;

  1. 在视口线程中声明用于绘制数据和UI元素的函数
void ViewportController::drawSomeStuff()
{
glMatrixMode(GL_PROJECTION);
/*get the appropriate cameras projection matrix*/
glMatrixMode(GL_MODELVIEW);
/*get the same cameras modelview matrix*/
/*Lots of stuff beginning with "gl" goes here*/
}
void ViewportController::drawSomeOtherStuff()
{
/*See above*/
}
void ViewportController::drawSomeDifferentOtherStuffMaybeAnEditBox()
{
/*See above*/
}

和一个";伪";ThreadController类中具有相同签名的函数;

void drawSomeStuff(){}
  1. 使用PostThreadMessage((将指向所需函数的指针传递到图形线程
typedef void (ViewportController::*RenderThreadFn)(); // declare pointer typedef in the ViewportController class
void ViewportController::size(){
RenderThreadFn pRenderFrame = &ViewportController::drawSomeStuff/*drawSomeOtherStuff*//*drawSomeDifferentOtherStuffMaybeAnEditBox*/;
PostThreadMessage(uiThreadID, UWM_DRAW, 0,reinterpret_cast<LPARAM(&pRenderFrame));
}
  1. 在图形线程中捕获PeekMessage((并执行函数
typedef void (ThreadController::*RenderThreadFn)(); // declare pointer typedef in the ThreadController class
ThreadController::draw(WPARAM wParam, LPARAM lParam){
RenderThreadFn* pRenderFrame = reinterpret_cast<RenderThreadFn*>(lParam); // get the pointer - everything up to here works 
(*this.*pRenderFrame)(); // Executing the function here does not work
}
  1. 给自己加薪,因为我什么都做得很好

这不起作用。显然,每个类中的指针都有不同的定义,但我不知道如何绕过这一点,也不知道如何才能获得相同的结果。

我将非常感谢任何关于这方面的指导,我已经尝试了一些事情,比如为视口和线程控制器类继承一个公共类,并在其中定义指针的typedef,但我仍然是一个初学者,我的问题解决并没有比随意敲击键盘更高级或更有效率。

关于如何做到这一点,有什么想法吗?能做到吗?这是个好主意吗?还是我应该用一种完全不同的方法来得到我想要的?如果可以的话,请帮忙,等我有钱出名的时候,我会记住你的。

PostMessage不是在线程之间整理函数指针和工作工作项的好方法。您最好只发明自己的线程安全数据结构,并将工作项排队到渲染线程。

但如果你真的想用PostMessage传递函数。。。则CCD_ 1与lambda的组合可以工作。

在一个通用头文件中声明:

struct MyCustomMessage
{
std::function<void()> fn;
};

在ViewPortController线程中;发送";对另一个线程的函数调用。

MyCustomMessage *pMsg = new MyCustomMessage();
pMsg->fn = [this](){
this->DrawSomeStuff();
};
PostThreadMessage(uiThreadID, UWM_DRAW, 0, reinterpret_cast<LPARAM>(pMsg);

在另一个线程中:

ThreadController::draw(WPARAM wParam, LPARAM lParam) {
MyCustomMessage* pMsg = reinterpret_cast<MyCustomMessage*>(lParam);
pMsg->fn();
delete pMsg;
}

您可以使用活动对象模式通过并发队列将数据发送到线程中。一个活动对象(查看维基百科条目(封装了线程函数和一个并发队列,用于将命令排入该线程。要发送命令,你可以这样做:

struct Command {
int id;
std::function<void()> fn;
Command(int id_, std::function<void()> &fn, size_t size){ ... }
};
MyActiveObject object;
Command *drawCmd = new Command(DrawUiEvent, func); 
object.enqueue(drawCmd);

这将有助于解决由于大量用户交互或其他原因而必须由线程函数进行大量更新的问题。

最新更新