c++ - 多线程减慢主线程
问题描述
我会尽力复制这个问题,因为它非常复杂。作为概述,我有一个使用 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
解决方案
这可能是也可能不是您的问题,但在这里:
//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
块之前进入第二个,导致等待该线程完成。LoadData
bool
在你创建线程之后,我会querying = true;
在主线程中设置LoadData
。另外,我会使用某种同步,例如声明querying
为atomic<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);
}
推荐阅读
- python - 使用 python pandas 中的列位置/编号转换为日期时间
- android - fetch SyntaxError: Unexpected token U in JSON at position 0 in react native Android
- php - 我想打印数字模式程序,但它不会给我错误的输出
- python - Azure App Service:在容器端指定 docker run 端口
- sql - Postgress to_date 和提取组合
- javascript - 使用 React 动态导入文件
- javascript - 仅在选项卡关闭时使用 beforeunload
- loops - 如何在不运行以下脚本的情况下退出循环
- sql - 尝试在 SQL DEVELOPER 上创建表时出现问题
- python - 可视化神经网络