You need to enable JavaScript to run this app. 如何实现SDL窗口与终端输入的同步响应?
优惠活动
大模型
产品
解决方案
定价
更多

如何实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.08 10:57:03