代码之家  ›  专栏  ›  技术社区  ›  Alexander Müller

C++中的线程间通信

  •  3
  • Alexander Müller  · 技术社区  · 15 年前

    我有两个线程(应用程序主线程和另一个线程)。我正在使用OpenGL绘制一些东西,我正在使用OpenGL键盘和鼠标回调。OpenGL在我调用glutMainLoop()时阻塞,由于我必须在后台进行一些计算,所以我创建了另一个线程。现在,OpenGL回调将向另一个具有关键部分的线程发送一些数据(例如,鼠标/键的x,y位置已按下)。虽然critical部分正在运行,但不应接受任何消息,而不是删除这些消息,我希望在critical部分之后处理它们。非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()的调用应该被推迟,因为它会扰乱整个计算。

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

    互斥.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/

    4 回复  |  直到 15 年前
        1
  •  4
  •   Kos    15 年前

    哦。。。不,不,不。 这里不应该用线。 说真的。在这种情况下,线程不是您的解决方案。让我们往后退一点。。。

    glutMainLoop() . 你不想锁定,因为你想同时做一些计算。

    停下来问问你自己- 您确定这些操作需要从OpenGL呈现异步(作为一个整体)完成吗? 如果是这样的话,您可能会停止阅读本文并查看其他文章,但我真诚地相信,对于典型的+实时OpenGL应用程序,情况可能并非如此。

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

    • 处理事件
    • 刻度计算
    • 重画屏幕

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

    您仍然可以在应用程序中引入并行性,但它应该在步骤2开始和停止,因此它在主循环级别上仍然是连续的:“计算一帧计算,然后呈现此帧”。这种方法很可能 省去你很多麻烦 .

    普罗蒂普:换你的图书馆。供过于求是过时的,不再维持了。切换到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更大),可能比每个渲染帧更为精确。

        2
  •  1
  •   Jeremy Friesner    15 年前

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

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

        3
  •  1
  •   Ben Voigt    15 年前

    关键部分和互斥体不好。它们应该只被库设计人员使用,而且通常不会被使用(因为对于可重用代码来说,获得无锁的额外可伸缩性通常是值得的)。

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

    • 线程消息队列( PostMessage )
    • 邮件槽
    • 消息模式管道
    • 数据报套接字
    • 滑动API

    只是你的一些选择。

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

        4
  •  1
  •   robinjam    15 年前

    我不建议再使用过剩-这是非常过时和限制性的。但如果你开始使用它,你可能会想调查一下 glutIdleFunc . 当这个回调空闲时,GLUT将继续调用它——您可以使用它在主线程中执行后台处理。

    推荐文章