开发者

Inter-thread communication in C++

I have two threads (the applications main thread and another one). I am using OpenGL to draw some stuff and I am using the OpenGL keyboard and mouse callbacks. OpenGL blocks when I call glutMainLoop() and since I have to do some calculations in the background, I created another thread. Now, the OpenGL callbacks shall send some data (e.g. x, y position of the mouse/key which has been pressed) to the other thread which has a critical section. While the critical section is running no messages should be accepted, but rather than dropping these messages, I want to process them after the critical section. The class of the non-OpenGL looks something like this:

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

So, if receiveMessage() is called from the OpenGL thread and processor->step() is running, the call to changeSomeData() should be postponed because it would mess up the whole calculation.

I want to use the following classes to synchronize the threads:

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()
{
    LeaveCrit开发者_StackOverflow中文版icalSection(&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();
}

EDIT:

Here is the whole project: http://upload.visusnet.de/uploads/BlobbyWarriors-rev30.zip (~180 MB)

EDIT 2:

And here is the SVN repo: https://projects.fse.uni-due.de/svn/alexander-mueller-bloby-warriors/trunk/


Oh... No, no, no. Threads are NOT what you should use here. Seriously. Thread are NOT your solution in this particular case. Let's roll back a bit...

You're using GLUT at the moment and you say you need threads to "avoid locking on glutMainLoop(). And you don't want locking because you want to do some calculations in the meantime.

Stop now and ask yourself - are you sure that those operations need to be done asynchronically (as a whole) from OpenGL rendering? If so, you may stop reading this post and look at the other ones, but I sincerely believe that it may not be the case for a +- typical real-time OpenGL application.

So... A typical OpenGL app looks like this:

  • handle events
  • tick calculations
  • redraw screen

Most GL window libraries let you implement that as your own main loop, GLUT kind of obfuscates that with its "callbacks", but the idea is the same.

You can still introduce parallelism in your application, but it should start and stop at step 2, so it's still sequential on main-loop level: "calculate a frame of calculations, THEN render this frame". This approach is likely to save you a lot of trouble.

Protip: Change your library. GLUT is outdated and not maintained anymore. Switching to GLFW (or SDL) for window creation wouldn't take much effort in terms of code and - contrary to GLUT - you define your main loop yourself, which seems to be what you want to achieve here. (Plus they tend to be more convenient for input & window event handling, etc.)


Some typical pseudocode with constant-timestep real-time physics without interfering with rendering (assuming that you want to run physics more often than rendering, in general):

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()
}

A possible extension from that is to use the value of accum as an additional "extrapolation" value only for rendering, which would allow for visually smooth the graphical representation while simulating physics more seldomly (with bigger DT), possibly more seldomly than once per rendering frame.


In the main thread: lock a mutex, add a struct/object containing the necessary info to a FIFO data structure of some sort, unlock the mutex, then (optionally) wake up the background thread (via a signal or a condition variable or writing a byte to a socket or however)

In the background thread: (optionally) block until awoken by the main thread, then lock the mutex, pop the first item from the head of the FIFO, unlock the mutex, process the item, repeat.


Critical sections and mutexes are bad. They should only be used by library designers, and usually not even then (because for reusable code, it's often worth the extra effort to gain the extra scalability of lock-free).

Instead, you should use a threadsafe queue. Windows offers lots:

  • thread message queue (PostMessage)
  • mailslots
  • message-mode pipes
  • datagram sockets
  • SList API

are just a few of your options.

All of these are highly optimized and much easier to use than designing your own queue.


I wouldn't recommend using GLUT anymore - it's terribly outdated and very restrictive. But if you're set on using it, you might want to look into glutIdleFunc. GLUT will continuously invoke this callback when it's idle - you can use this to perform background processing in the main thread.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜