我有两个线程(应用程序主线程和另一个线程)。我正在使用 OpenGL 绘制一些东西,我正在使用 OpenGL 键盘和鼠标回调。当我调用 glutMainLoop() 时 OpenGL 会阻塞,因为我必须在后台进行一些计算,所以我创建了另一个线程。现在,OpenGL 回调应将一些数据(例如,已按下的鼠标/键的 x、y 位置)发送到具有关键部分的另一个线程。当关键部分正在运行时,不应接受任何消息,但我不想丢弃这些消息,而是想在关键部分之后处理它们。非 OpenGL 类看起来像这样:
void run()
{
for (;;) {
int currentTime = now();
if (now() - previousTime > WAIT_INTERVAL) {
previousTime = currentTime;
tick();
}
}
}
void tick() {
// critical section begins
processor->step()
// critical section ends
}
void receiveMessage(void *data) {
processor->changeSomeData(data);
}
因此,如果从 OpenGL 线程调用 receiveMessage() 并且 processor->step() 正在运行,则应推迟对 changeSomeData() 的调用,因为它会扰乱整个计算。
我想使用以下类来同步线程:
互斥锁.h:
#ifndef MUTEX_H
#define MUTEX_H
#include <Windows.h>
class Mutex;
#include "Lock.h"
class Mutex
{
public:
Mutex();
~Mutex();
private:
void acquire();
void release();
CRITICAL_SECTION criticalSection;
friend class Lock;
};
#endif
互斥锁.cpp:
#include "Mutex.h"
Mutex::Mutex()
{
InitializeCriticalSection(&this->criticalSection);
}
Mutex::~Mutex()
{
DeleteCriticalSection(&this->criticalSection);
}
void Mutex::acquire()
{
EnterCriticalSection(&this->criticalSection);
}
void Mutex::release()
{
LeaveCriticalSection(&this->criticalSection);
}
锁.h:
#ifndef LOCK_H
#define LOCK_H
class Lock;
#include "Mutex.h"
class Lock
{
public:
Lock(Mutex& mutex);
~Lock();
private:
Mutex &mutex;
};
#endif
锁.cpp
#include "Lock.h"
Lock::Lock(Mutex& mutex) : mutex(mutex)
{
this->mutex.acquire();
}
Lock::~Lock ()
{
this->mutex.release();
}
编辑:
这是整个项目:http://upload.visusnet.de/uploads/BlobbyWarriors-rev30.zip (~180 MB)
编辑 2:
这是 SVN 存储库:https://projects.fse.uni-due.de/svn/alexander-mueller-bloby-warriors/trunk/
最佳答案
哦……不,不,不。 这里不应该使用线程。说真的。在这种特殊情况下,线程不是您的解决方案。让我们回滚一下......
你现在正在使用 GLUT,你说你需要线程来“避免在 glutMainLoop()
上锁定。你不想锁定,因为你想同时做一些计算.
现在停下来问问自己 - 您确定这些操作需要从 OpenGL 渲染中异步(作为一个整体)完成吗?如果是这样,您可能会停止阅读这篇文章并查看其他文章一个,但我真诚地相信,对于一个 +- 典型的实时 OpenGL 应用程序,情况可能并非如此。
所以...典型的 OpenGL 应用程序如下所示:
- 处理事件
- 报价计算
- 重绘屏幕
大多数 GL 窗口库都允许您将其实现为您自己的主循环,GLUT 有点混淆了它的“回调”,但想法是一样的。
您仍然可以在您的应用程序中引入并行性,但它应该在第 2 步开始和停止,因此它在主循环级别上仍然是顺序的:“计算一个计算帧,然后渲染这个帧”。这种方法很可能为您省去很多麻烦。
提示:改变你的图书馆。 GLUT 已过时且不再维护。切换到 GLFW(或 SDL)来创建窗口不会在代码方面花费太多精力,而且 - 与 GLUT 相反 - 你自己定义主循环,这似乎是你想要在这里实现的。 (此外,它们往往更便于输入和窗口事件处理等)
一些典型的伪代码,具有恒定时间步长的实时物理而不干扰渲染(假设您希望运行物理比渲染更频繁,一般来说):
var accum = 0
const PHYSICS_TIMESTEP = 20
while (runMainLoop) {
var dt = getTimeFromLastFrame
accum += dt
while (accum > PHYSICS_TIMESTEP) {
accum -= PHYSICS_TIMESTEP
tickPhysicsSimulation(PHYSICS_TIMESTEP)
}
tickAnyOtherLogic(dt)
render()
}
一个可能的扩展是将 accum
的值用作仅用于渲染的附加“外推”值,这将允许在视觉上平滑图形表示,同时很少模拟物理(使用更大的DT),每个渲染帧可能不止一次。
关于c++ - C++中的线程间通信,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4230559/