C ++中的线程间通信

时间:2010-11-20 00:29:28

标签: c++ visual-studio multithreading opengl glut

我有两个线程(应用程序主线程和另一个线程)。我正在使用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()并且处理器 - > step()正在运行,则应该推迟对changeSomeData()的调用,因为它会搞乱整个计算。

我想使用以下类来同步线程:

Mutex.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

Mutex.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);
}

Lock.h:

#ifndef LOCK_H
#define LOCK_H

class Lock;

#include "Mutex.h"

class Lock
{
public:
    Lock(Mutex& mutex);
    ~Lock();
private:
    Mutex &mutex;
};

#endif

Lock.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/

4 个答案:

答案 0 :(得分:4)

哦......不,不,不。 线程不是你应该在这里使用的。严肃地说。在这种特殊情况下,线程不是您的解决方案。让我们回滚一下......

你现在正在使用GLUT而且你说你需要线程来“避免锁定glutMainLoop()。而你不想锁定,因为你想在此期间进行一些计算。”

现在停下来问问自己 - 你确定那些操作需要从OpenGL渲染异步(整体)完成吗?如果是这样,你可能会停止阅读这篇文章并看看另一篇文章那些,但我真诚地相信它可能不是典型的实时OpenGL应用程序。

所以...典型的OpenGL应用程序如下所示:

  • 处理事件
  • 勾选计算
  • 重绘屏幕

大多数GL窗口库允许您将其实现为您自己的主循环,GLUT通过其“回调”来混淆,但这个想法是相同的。

您仍然可以在应用程序中引入并行性,但它应该在步骤2开始和停止,因此它仍然在主循环级别上顺序:“计算一帧计算,然后渲染此帧”。这种方法很可能为您节省很多麻烦

Protip:更改你的图书馆。 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),每个渲染帧可能比一次更少。

答案 1 :(得分:1)

在主线程中:锁定互斥锁,将包含必要信息的结构/对象添加到某种FIFO数据结构中,解锁互斥锁,然后(可选)唤醒后台线程(通过信号或条件)变量或将字节写入套接字或())

在后台线程中:(可选)阻塞直到被主线程唤醒,然后锁定互斥锁,弹出FIFO头部的第一项,解锁互斥锁,处理项目,重复。

答案 2 :(得分:1)

关键部分和互斥体都很糟糕。它们应该只供图书馆设计人员使用,通常情况下也是如此(因为对于可重复使用的代码,通常需要额外的努力来获得无锁的额外可扩展性)。

相反,您应该使用线程安全队列。 Windows提供很多:

  • 线程消息队列(PostMessage
  • 邮筒
  • 消息模式管道
  • 数据报套接字
  • SList API

只是您的一些选择。

所有这些都经过高度优化,比设计自己的队列更容易使用。

答案 3 :(得分:1)

我不建议再使用GLUT了 - 它非常过时且非常严格。但是如果您已开始使用它,您可能需要查看glutIdleFunc。 GLUT将在空闲时不断调用此回调 - 您可以使用它在主线程中执行后台处理。