如何实现SDL窗口与终端输入的同步响应?
问题根因
- 主线程被
SDL_WaitThread阻塞:主循环中调用该函数会让主线程彻底暂停等待输入,无法处理SDL窗口的系统事件(拖动、重绘、关闭请求等),操作系统检测到长时间未响应事件就会标记窗口无响应。 - 线程重复创建销毁:每次循环都新建输入线程,资源开销高,线程销毁不及时可能引发内存或句柄泄漏,最终导致程序崩溃。
- 未消费SDL事件队列:SDL要求主线程必须定期遍历事件队列,即使不需要处理任何交互事件也需要清空队列,这是SDL窗口正常运行的必要条件。
优化实现方案
核心思路是单独启一个常驻输入线程,用线程安全的方式传递输入指令,主循环不阻塞,优先处理SDL事件。
修改后的可运行代码如下:
/* g++ test.cpp -o test -lSDL2 -pthread */ #include <iostream> #include <string> #include <vector> #include <cstdlib> #include <ctime> #include <atomic> #define SDL_MAIN_HANDLED #include <SDL2/SDL.h> #include <SDL2/SDL_render.h> using namespace std; const int width = 160; const int height = 144; // 线程安全的命令存储,-1代表无新命令,0是重绘,1是退出 atomic<int> g_cmd = -1; static int inputThread(void *a) { while (g_cmd != 1) { int in; cout << ">> "; cin >> in; // 只有当前没有待处理命令时才更新,避免输入丢失 while (g_cmd != -1) { SDL_Delay(10); } g_cmd = in; } return 0; } int main() { std::srand(unsigned(std::time(NULL))); SDL_Init(SDL_INIT_EVERYTHING); SDL_Window* window = SDL_CreateWindow( "SDL2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width * 3, height * 3, SDL_WINDOW_SHOWN); SDL_Renderer* renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED); SDL_Texture* texture = SDL_CreateTexture( renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height); vector<uint8_t> pixels(width * height * 4, 0); cout << "0 - rerender; 1 - quit the program" << endl; // 启动常驻输入线程 SDL_Thread *thread = SDL_CreateThread(inputThread, "InputThread", (void *) NULL); if (thread == NULL) { printf("SDL_CreateThread failed: %s\n", SDL_GetError()); return -1; } bool running = true; while(running) { // 先处理SDL所有事件,保证窗口响应 SDL_Event e; while (SDL_PollEvent(&e)) { if (e.type == SDL_QUIT) { running = false; g_cmd = 1; } } // 检查有没有新的控制台命令 if (g_cmd != -1) { int cmd = g_cmd; g_cmd = -1; // 重置命令状态,接收下一次输入 if (cmd == 0) // rerender { SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); // splat down some random pixels for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { int offset = i * width * 4 + j * 4; int r = std::rand() % 256; int g = std::rand() % 256; int b = std::rand() % 256; pixels[offset] = b; pixels[offset + 1] = g; pixels[offset + 2] = r; pixels[offset + 3] = 255; } } SDL_UpdateTexture( texture, NULL, &pixels[0], width * 4); SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); } else if (cmd == 1) // quit { running = false; } } // 避免主循环空转占满CPU SDL_Delay(16); } // 等待输入线程正常退出 SDL_WaitThread(thread, NULL); SDL_DestroyRenderer(renderer); renderer = NULL; SDL_DestroyWindow(window); window = NULL; SDL_Quit(); return 0; }
关键优化点说明
- 用
std::atomic<int>做跨线程命令传递,保证线程安全不需要额外加锁 - 输入线程改为常驻,仅在程序退出时销毁,避免重复创建的开销
- 主循环优先调用
SDL_PollEvent消费所有事件,保证窗口随时响应系统操作 - 主循环增加16ms延时,避免空转占用100%CPU资源
- 支持点击窗口关闭按钮正常退出,和控制台输入1退出的逻辑统一
内容的提问来源于stack exchange,提问作者The Rizzler
相关产品推荐
相关产品推荐

