首页 > 解决方案 > 多线程减慢主线程

问题描述

我会尽力复制这个问题,因为它非常复杂。作为概述,我有一个使用 OpenGL 显示图形的程序。这是线程特定的,所以我知道渲染只在一个线程上完成。我的另一个线程查询数据库并将数据存储在复制向量中。线程完成后,它将数据与 OpenGL 线程正在使用的数据交换(在将线程与主线程连接后)。理论上没有什么应该让程序运行这么慢?

最奇怪的部分是它最终如何“预热”并在一段时间后运行得更快(它变化很大,有时几乎是瞬间,有时在运行 30 秒后)。从价值的角度进行比较,程序在查询数据时开始以大约 30-60 fps 的速度运行(例如,不断加载它并交换它并加入线程),但是一旦它预热,它就会以 1000 fps 的速度运行.

我已经测试了一些东西,首先是让查询需要很长时间才能运行。在此期间,fps 处于最大值(3000+)。只有当数据不断变化(交换向量)时,才会开始运行得很慢。仅这一点导致性能下降是没有意义的,因为它在“热身”后运行得非常好。

编辑:

我已经设法制作了一个合理的最小可重现示例,并且我发现了一些有趣的结果。

这是代码:

#include <iostream>
#include <string>
#include <thread>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "ImGui/imgui.h"
#include "ImGui/imgui_impl_glfw.h"
#include "ImGui/imgui_impl_opengl3.h"

bool querying = false;
std::thread thread;
int m_Window_Width = 1280;
int m_Window_Height = 720;


static void LoadData()
{
    querying = true;
    
    std::this_thread::sleep_for(std::chrono::milliseconds(100));

    querying = false;
}



int main()
{


    glfwInit();

    const char* m_GLSL_Version = "#version 460";
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
    GLFWwindow* m_Window = glfwCreateWindow(m_Window_Width, m_Window_Height, "Program", NULL, NULL);
    glfwMakeContextCurrent(m_Window);
    glfwSwapInterval(0); //  vsync

    glewInit();

    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGui::StyleColorsClassic();

    // Setup Platform/Renderer backends
    ImGui_ImplGlfw_InitForOpenGL(m_Window, true);
    ImGui_ImplOpenGL3_Init(m_GLSL_Version);



    thread = std::thread(LoadData);


    while (!glfwWindowShouldClose(m_Window))
    {
        glfwPollEvents();
        
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();


        char fps[12];
        sprintf_s(fps, "%f", ImGui::GetIO().Framerate);
        glfwSetWindowTitle(m_Window, fps);


        //Load the data
        if (thread.joinable() == false && querying == false) {
            thread = std::thread(LoadData);
        }
        //Swap the data after thread is finished
        if (thread.joinable() == true && querying == false) {
            thread.join();
        }

        // Rendering
        ImGui::Render();
        glfwGetFramebufferSize(m_Window, &m_Window_Width, &m_Window_Height);
        glViewport(0, 0, m_Window_Width, m_Window_Height);
        glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
        glClear(GL_COLOR_BUFFER_BIT);
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
        glfwSwapBuffers(m_Window);
    }





    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();

    glfwDestroyWindow(m_Window);
    glfwTerminate();


    

    return 0;
}

现在有趣的是玩弄std::this_thread::sleep_for(). 我已经实现了这个,所以我可以模拟在主数据库上运行查询时实际需要的速度。有趣的是,它实际上导致主线程停止运行并冻结它。这些线程应该是分开的并且不会相互影响,但是这里不是这种情况。对此有什么解释吗?这似乎是我的主程序的根本问题,归结为这一点。

编辑 2

要使用库(在 Visual Studio 中),请从此处下载二进制文件https://www.glfw.org/download.html以及此处http://glew.sourceforge.net/,最后ImGui从此处下载,https://github.com/ocornut/imgui

预处理器: GLEW_STATIC; WIN32;

链接器: glfw3.lib;glew32s.lib;opengl32.lib;Gdi32.lib;Shell32.lib;user32.lib;Gdi32.lib

标签: c++databasemultithreadingperformance

解决方案


这可能是也可能不是您的问题,但在这里:

    //Load the data
    if (thread.joinable() == false && querying == false) {
        thread = std::thread(LoadData);
    }
    //Swap the data after thread is finished
    if (thread.joinable() == true && querying == false) {
        thread.join();
    }

有可能您在第一个块中启动线程,然后在修改该if块之前进入第二个,导致等待该线程完成。LoadDatabool

在你创建线程之后,我会querying = true;在主线程中设置LoadData。另外,我会使用某种同步,例如声明queryingatomic<bool>.

编辑:

看来您不需要检查joinable()- 您知道线程何时可加入:当您进入循环时,以及在您重新启动该线程之后。这看起来更干净:

std::atomic<bool> querying = true;

void LoadData()
{
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    querying = false;
}

稍后在您的循环中:

//Swap the data after thread is finished
if (!querying) {
    thread.join();
    querying = true;
    thread = std::thread(LoadData);
}

推荐阅读