C++ 带互斥锁的多线程输入不平滑(如预期)

C++ 带互斥锁的多线程输入不平滑(如预期),c++,multithreading,c++11,input,C++,Multithreading,C++11,Input,我目前正在从头开始开发一个3D引擎,因为我想使用更现代的技术,坦白地说,我以前的设计是垃圾。现在我正在实现我的输入线程 现在我有了更多的经验,我知道如果我从输入线程和渲染/主线程写入相同的变量,那么我将获得数据竞争,所以我决定使用互斥?锁定可以在不同线程中写入的数据,但这会导致无法接受的错误:鼠标输入不再平滑:/ 我确实有点期待,不过,我只是觉得我的想法可能是错的 现在我被困在十字路口,因为我不知道如何着手解决这个问题 我从两个线程写入的变量是x_rel和y_rel,这是相对于我收到事件时最后一

我目前正在从头开始开发一个3D引擎,因为我想使用更现代的技术,坦白地说,我以前的设计是垃圾。现在我正在实现我的输入线程

现在我有了更多的经验,我知道如果我从输入线程和渲染/主线程写入相同的变量,那么我将获得数据竞争,所以我决定使用互斥?锁定可以在不同线程中写入的数据,但这会导致无法接受的错误:鼠标输入不再平滑:/

我确实有点期待,不过,我只是觉得我的想法可能是错的

现在我被困在十字路口,因为我不知道如何着手解决这个问题

我从两个线程写入的变量是x_rel和y_rel,这是相对于我收到事件时最后一个位置的鼠标位置

输入线程设置变量,渲染/主线程在使用完变量后将其重置为0.0。这很好,但正如我所说,这给了我非常严格的鼠标运动

我的问题是,在线程间保持安全竞争的情况下,如何才能获得平滑的输入

这是我的互斥定义,它是全局的:

下面是我用来获取鼠标事件的代码:

这是我的主要功能:


这是我对你问题的理解:

基本上,您有两个线程,一个处理来自系统的鼠标事件,另一个负责渲染任务。据我所知,后者需要最新的鼠标位置,以便计算一组精确的相机矩阵。但是,由于输入设备的性质,事件线程中充斥着鼠标位置事件:系统轮询设备的速度足以在每个渲染帧中获得许多更新,并且这些更新被推回事件线程。该线程只有该任务要执行,它将在处理这些事件时不断锁定/解锁鼠标互斥锁,并且当渲染线程实际想要获得该锁时,事件线程持有该锁的几率很高

当前设置可能会出现两个问题:

要么渲染线程需要所有鼠标更新,因此您需要在该线程和事件线程之间实现一个事件队列来跟踪所有这些更新,然后您将本质上过滤鼠标事件以将它们分派到渲染:

在这些线程之间设置鼠标事件队列,只需将鼠标事件从输入队列推送到新队列。 让渲染在每一帧检查鼠标队列,并根据需要进行尽可能多的相机更新,或者更好——如评论中所建议的,如果可能,将所有这些事件合并到一个相机更新中。您应该尝试将该计算放在事件线程中,以减少渲染的负载。 或者渲染只需要最新的事件,在这种情况下,您只需要使用最新的事件更新互斥数据结构,从而减少互斥上的争用

总而言之,这取决于相机更新功能的工作方式。鼠标事件,您可能必须将其更改为仅使用从所有相对事件累积生成的一组绝对坐标,或者您可以直接从鼠标事件结构?获取它们,并且每个事件流仅更新一次静音数据

注: 您还可以查看其他引擎是如何做到这一点的:IdTech2 Quake I/II系列引擎是单线程的,但它们仍然是一个很好的灵感来源。每一帧,这些 一次性处理所有鼠标输入。在帧渲染期间,将在_Move、HandleEvents或其他函数中调用第一个例程,具体取决于后端,请参阅sys*文件以检查所有事件并更新所有相关结构。当调用渲染代码R_RenderFrame时,这些结构上不再存在争用。您可能希望通过确保渲染不会被一个或多个互斥锁阻止来模拟相同的行为。上面已经描述了一种可能的鼠标输入解决方案,当然可以扩展到处理其他类型的输入设备。

为什么要使用try\u lock?@pmjordan,这样我的主线程就不会阻止渲染,这样我的输入线程就可以在无法锁定鼠标输入互斥锁的情况下获得其他输入。您是否测量了两个站点上try\u lock调用实际成功的百分比,以及测量了阻止版本的成本?我怀疑这就是你的问题的根源。让互斥锁阻塞通常不是一件坏事,只要将其保持在尽可能短的时间内即可。你的主互斥锁可以很快释放出来的更新_相机调用了it@pmjordan它给出了几乎相同的结果,只是稍微有点僵硬的鼠标运动。目前,我根本不使用互斥锁,我真的更愿意在与种族公司的比赛中保持安全 条件。你的app.get_输入的频率;在工作线程上运行时返回,而在主线程上运行时返回?取决于操作系统、事件处理的类型等。您可能会发现事件在主线程上处理,而从另一个线程查询事件只会增加同步开销和排队延迟。
std::mutex mouse_mutex;
void input_thread_func(application &app, const bool &running, double &x_rel, double &y_rel){
    while(running){
        application::event ev = app.get_input();
        switch(ev.type){
            case events::mouse_motion :{
                if(mouse_mutex.try_lock()){
                    x_rel = ev.xrel;
                    y_rel = ev.yrel;
                    mouse_mutex.unlock();
                }
                break;
            }

            default:
                break;
        }
    }
}
int main(int argc, char *argv[]){
    /* all my init stuff */

    application app;
    bool running = true;
    double x_rel = 0.0, y_rel = 0.0;
    std::thread input_thread(
        input_thread_func,
        std::ref(app), std::cref(running)
        std::ref(x_rel), std::ref(y_rel)
    );

    double multiplier = /* whatever I like */;        

    while(running){
        /* check input data */

        if(mouse_mutex.try_lock()){
            update_camera(x_rel * multiplier, y_rel * multiplier);            
            app.set_mouse_pos(0, 0);
            x_rel = 0.0; y_rel = 0.0;
            mouse_mutex.unlock();
        }

        /* do rendering stuff */
    }
}