将指向成员函数的指针传递给单独的线程
Posted
技术标签:
【中文标题】将指向成员函数的指针传递给单独的线程【英文标题】:Passing pointer to a member function to a seperate thread 【发布时间】:2020-09-19 23:25:40 【问题描述】:我正在使用 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 参数传递; p>
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;
目前,我将所有自定义 UI 元素(文本、编辑框等)都包含在 ThreadController 类中。这使得 UI 的动态创建变得非常困难,更重要的是破坏了拥有仅图形管道的对象,所以我想做的是这个;
-
在视口线程中声明绘制数据和 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()
-
使用 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));
-
在图形线程中捕捉 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
-
给自己加薪,因为自己什么都擅长。
这不起作用。显然每个类中的指针都有不同的定义,但我不知道如何解决这个问题,或者我如何才能达到相同的结果。
我将非常感谢任何关于这方面的指导,我已经尝试了一些事情,比如为视口和线程控制器类继承一个公共类并为其中的指针定义 typedef,但我仍然非常初学者,我的问题解决方法并不比随意敲击键盘更先进或更有效率。
关于如何做到这一点的任何想法?可以做到吗?这是一个好主意还是我应该使用完全不同的方法来获得我想要的东西?可以的话请帮忙,等我有钱有名的时候会记得你的。
【问题讨论】:
【参考方案1】:PostMessage 不是在线程之间编组函数指针和工作工作项的好方法。你最好只发明自己的线程安全数据结构并将工作项排队到渲染线程。
但是,如果您真的一心想要使用 PostMessage 传递函数...那么 std::function
与 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;
【讨论】:
假设您确保足够的生命周期,postMessaging 对其他线程的整个对象引用没有问题。您可以新建/处理它们,或者更好地创建一个池,以便它们可以回收。【参考方案2】:您可以使用 Active-Object-Pattern 通过 Concurrent-Queue 将数据发送到您的线程。一个活动对象(检查Wikipedia 条目)封装了线程函数和一个并发队列以将命令排入该线程。要发送命令,您可以这样:
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);
当由于大量用户交互或其他原因而必须由您的线程函数进行大量更新时,这将有助于解决问题。
【讨论】:
以上是关于将指向成员函数的指针传递给单独的线程的主要内容,如果未能解决你的问题,请参考以下文章
我应该如何将指向成员函数的指针传递给 OpenCV 中的 setMouseCallback?