SDL2:有时在不同线程中进行渲染和事件轮询时会出现段错误

SDL2: Sometimes make segement fault while Rendering and Event Polling in the different thread

所以在我的项目中,我创建了一个事件线程来捕获 sdl 事件,并可能将其传递给主线程以进行排序。但有时我会遇到 Segementfault。 这是我的测试代码。

#include "SDL2/SDL.h"

#include <SDL2/SDL_timer.h>
#include <iostream>
#include <thread>

using namespace std;

int main() {
    SDL_Init(SDL_INIT_EVERYTHING);

    bool running = true;
    /* this is my event thread */
    auto run = [&] {
        for (; running; ) {
            SDL_Event event; SDL_PollEvent(&event);
            // ...
            SDL_Delay(10);
        }
    };
    thread t(run);

    /* and the main is the randering thread */
    for (int i = 0; i < 10; ++i) {
        SDL_Window *window = SDL_CreateWindow("cycle. ", 
                                            100,
                                            100,
                                            600,
                                            600,
                                            SDL_WINDOW_SHOWN);

        SDL_Renderer *screen = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
        
        // ... do some rander ...
        // may be i will get some task to rander use the blocking queue

        SDL_Delay(500);
        SDL_DestroyRenderer(screen);
        SDL_DestroyWindow(window);

    }

    running = false;
    t.join();

    SDL_Quit();
}

从根本上说,您应该尝试从单个线程调用 SDL。即使你决定你的程序需要多线程,你也应该在其他线程中工作,然后将该工作同步到主线程,主线程应该使用 SDL 来呈现/事件处理等。

你的程序可能是段错误,因为你试图join()一个已经join()-ed的线程,所以你至少要检查一下joinable() 在你这样做之前。

您还需要循环调用 PollEvent,因为这里可能有多个事件排队。

但是,特别是因为您正在使用延迟,您似乎不需要通过多线程处理您的事件来获得可能的性能。因此,我会建议这样的事情:

#include <SDL2/SDL.h>

#include <iostream>
#include <thread>

using namespace std;

int main() {
    SDL_Init(SDL_INIT_EVERYTHING);

    bool running = true;

    SDL_Window *window =
        SDL_CreateWindow("cycle. ", 100, 100, 600, 600, SDL_WINDOW_SHOWN);

    SDL_Renderer *screen =
        SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);

    /* and the main is the randering thread */
    while (running) {
        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            // TODO: handle events
            // for example, set `running` to false when the window is closed or
            // escape is pressed
            // ...
        }

        // TODO: render here
        // ...
    }

    SDL_DestroyRenderer(screen);
    SDL_DestroyWindow(window);

    SDL_Quit();
}

我不会假设你做什么和不需要什么,所以这里有一个可能的解决方案,你可以对事件进行多线程处理(但你真的,真的,真的应该只在你绝对需要的时候使用它)。

这与您的代码的作用相同,只是它在与呈现相同的线程(主线程)中调用 PollEvent,但在其他地方处理事件。

为此,我们使用一个事件队列和一个互斥体来确保它是线程安全的。为了完整起见,我把它放在这里,你可能(绝对)不需要它。

#include <SDL2/SDL.h>

#include <iostream>
#include <mutex>
#include <queue>
#include <thread>

using namespace std;

int main() {
    SDL_Init(SDL_INIT_EVERYTHING);

    bool running = true;

    std::mutex eventQueueMutex;
    std::queue<SDL_Event> eventQueue;
    auto handleEvents = [&running, &eventQueue, &eventQueueMutex] {
        while (running) {
            {  // scope for unique_lock
                std::unique_lock<std::mutex> lock(eventQueueMutex);
                if (!eventQueue.empty()) {
                    auto event = eventQueue.front();
                    eventQueue.pop();
                    lock.unlock();
                    // TODO: handle the event here
                }
            }  // end of scope for unique_lock
            std::this_thread::sleep_for(
                std::chrono::milliseconds(16));  // adjust this to your needs
        }
    };

    std::thread eventHandlerThread = std::thread(handleEvents);

    SDL_Window *window =
        SDL_CreateWindow("cycle. ", 100, 100, 600, 600, SDL_WINDOW_SHOWN);

    SDL_Renderer *screen =
        SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);

    /* and the main is the randering thread */
    while (running) {
        SDL_Event event;
        eventQueueMutex.lock();
        while (SDL_PollEvent(&event)) {
            eventQueue.push(event);  // copy event into queue
        }
        eventQueueMutex.unlock();

        // TODO: render here
        // ...
    }

    if (eventHandlerThread.joinable()) {
        eventHandlerThread.join();
    }

    SDL_DestroyRenderer(screen);
    SDL_DestroyWindow(window);

    SDL_Quit();
}

在这两个示例中,请查看我的 TODO 注释以了解您在何处编写代码。