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();
}
- 我想知道为什么会出现段错误。
- 我应该让 Event listening 和 rander 在同一个 thead 中吗? (这会导致另一个问题:如果放在同一个线程中,当我把循环设置为每秒只有60次时,也意味着我每秒只能渲染60次。这会导致我的程序卡死。)
从根本上说,您应该尝试从单个线程调用 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
注释以了解您在何处编写代码。
所以在我的项目中,我创建了一个事件线程来捕获 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();
}
- 我想知道为什么会出现段错误。
- 我应该让 Event listening 和 rander 在同一个 thead 中吗? (这会导致另一个问题:如果放在同一个线程中,当我把循环设置为每秒只有60次时,也意味着我每秒只能渲染60次。这会导致我的程序卡死。)
从根本上说,您应该尝试从单个线程调用 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
注释以了解您在何处编写代码。