【问题标题】:C++11 + SDL2 + Windows: Multithreaded program hangs after any input eventC++11 + SDL2 + Windows:多线程程序在任何输入事件后挂起
【发布时间】:2014-02-27 03:30:33
【问题描述】:

我正在使用 C++11、MinGW 和 Windows API 开发屏幕捕获程序。我正在尝试使用 SDL2 实时观察我的屏幕捕获程序的工作方式。

窗口可以正常打开,只要我只是移动鼠标光标,程序似乎运行良好。但是如果我在窗口中单击,它的菜单栏,在窗口外,或者按任意键,SDL 窗口就会冻结。

我已经为事件设置了一些日志记录,以了解正在发生的事情。除了SDL_WINDOW_FOCUS_GAINEDSDL_TEXTEDITINGSDL_WINDOWEVENT_SHOWN 以外,我从来没有按此顺序收到任何事件。所有这些都是在开始时收到的。

我试图找到有关 SDL 事件处理的教程,因为这是我对问题根源的最佳猜测。我发现除了基本的事件处理来观察SDL_QUIT,基本的鼠标和键盘事件,以及SDL_WINDOWEVENTs 上的一个似乎没有帮助。我没有深入了解这些事件的含义以及处理它们的最佳实践。这可能无关紧要,因为这可能不是问题的根源。据我所知,由于还有其他线程在运行,所以 SDL 正在崩溃。

谁能在我的代码中看到导致此问题的任何原因并解释如何解决它?

对我的程序结构的快速解释是为了涵盖我省略的代码。 Captor 类启动并运行一个线程来抓取屏幕截图以传递给EncoderEncoder 启动可变数量的线程,这些线程从Captor 接收屏幕截图,对屏幕截图进行编码,然后将编码传递给Screen。传递机制是SynchronousQueue<T> 类,它提供配对方法put(const T&)T get() 以允许生产者和消费者使用资源进行同步;这些方法超时以允许系统响应终止消息。

现在是源文件(希望没有太多臃肿)。 虽然我会感谢任何有关如何提高应用程序性能的 cmets,但我的重点是使程序具有响应性。

ma​​in.cpp

#include "RTSC.hpp"

int main(int argc, char** argv) {
    RTSC rtsc {
        (uint32_t) stoi(argv[1]),
        (uint32_t) stoi(argv[2]),
        (uint32_t) stoi(argv[3]),
        (uint32_t) stoi(argv[4]),
        (uint32_t) stoi(argv[5]),
        (uint32_t) stoi(argv[6])
    };

    while (rtsc.isRunning()) {
        SwitchToThread();
    }

    return 0;
}

RTSC.hpp

#ifndef RTSC_HPP
#define RTSC_HPP

#include "Captor.hpp"
#include "Encoder.hpp"
#include "Screen.hpp"

#include <iostream>
using namespace std;


class RTSC {
    private:
        Captor *captor;
        Encoder *encoder;

        SynchronousQueue<uint8_t*> imageQueue {1};
        SynchronousQueue<RegionList> regionQueue {1};

        Screen *screen;

    public:
        RTSC(
            uint32_t width,
            uint32_t height,
            uint32_t maxRegionCount,
            uint32_t threadCount,
            uint32_t divisionsAlongThreadWidth,
            uint32_t divisionsAlongThreadHeight
        ) {
            captor = new Captor(width, height, imageQueue);
            encoder = new Encoder(
                width,
                height,
                maxRegionCount,
                threadCount,
                divisionsAlongThreadWidth,
                divisionsAlongThreadHeight,
                imageQueue,
                regionQueue
            );

            screen = new Screen(
                width,
                height,
                width >> 1,
                height >> 1,
                regionQueue
            );

            captor->start();
        }

        ~RTSC() {
            delete screen;
            delete encoder;
            delete captor;
        }

        bool isRunning() const {
            return screen->isRunning();
        }
};

#endif

Screen.hpp

#ifndef SCREEN_HPP
#define SCREEN_HPP

#include <atomic>
#include <SDL.h>
#include <windows.h>

#include "Region.hpp"
#include "SynchronousQueue.hpp"

using namespace std;

class Screen {
    private:
        atomic_bool running {false};
        HANDLE thread;
        SynchronousQueue<RegionList>* inputQueue;
        uint32_t inputHeight;
        uint32_t inputWidth;
        uint32_t screenHeight;
        uint32_t screenWidth;

        SDL_Renderer* renderer;
        SDL_Surface* surface;
        SDL_Texture* texture;
        SDL_Window* window;

        void run() {
            SDL_Event event;
            while (running) {
                while (SDL_PollEvent(&event)) {
                    switch (event.type) {
                        case SDL_QUIT:
                            running = false;
                            break;

                        case SDL_WINDOWEVENT:
                            switch (event.window.event) {
                                case SDL_WINDOWEVENT_CLOSE:
                                    running = false;
                                    break;

                        default:
                            break;
                    }
                }

                try {
                    RegionList rl = inputQueue->get();

                    SDL_RenderClear(renderer);

                    SDL_LockSurface(surface);
                    SDL_FillRect(surface, nullptr, 0);

                    for (uint32_t i = 0; i < rl.count; ++i) {
                        Region &r = rl.regions[i];

                        SDL_Rect rect {
                            (int) r.getX(),
                            (int) r.getY(),
                            (int) r.getWidth(),
                            (int) r.getHeight()
                        };
                        uint32_t color =
                            (r.getRed() << 16) +
                            (r.getGreen() << 8) +
                            r.getBlue();
                        SDL_FillRect(surface, &rect, color);
                    }

                    SDL_UnlockSurface(surface);
                    SDL_UpdateTexture(
                        texture,
                        nullptr,
                        surface->pixels,
                        surface->pitch
                    );
                    SDL_RenderCopyEx(
                        renderer,
                        texture,
                        nullptr,
                        nullptr,
                        0,
                        nullptr,
                        SDL_FLIP_VERTICAL
                    );
                } catch (exception &e) {}

                SDL_RenderPresent(renderer);
                SwitchToThread();
            }
        }

        static DWORD startThread(LPVOID self) {
            ((Screen*) self)->run();
            return (DWORD) 0;
        }

    public:
        Screen(
            uint32_t inputWidth,
            uint32_t inputHeight,
            uint32_t windowWidth,
            uint32_t windowHeight,
            SynchronousQueue<RegionList> &inputQueue
        ): inputQueue {&inputQueue}, inputHeight {inputHeight} {
            SDL_Init(SDL_INIT_VIDEO);

            window = SDL_CreateWindow(
                "RTSC",
                SDL_WINDOWPOS_CENTERED,
                SDL_WINDOWPOS_CENTERED,
                windowWidth,
                windowHeight,
                SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE |
                    SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS
            );

            renderer = SDL_CreateRenderer(window, -1, 0);

            surface = SDL_CreateRGBSurface(
                0,
                inputWidth,
                inputHeight,
                24,
                0xFF << 16,
                0xFF << 8,
                0xFF,
                0
            );

            texture = SDL_CreateTexture(
                renderer,
                surface->format->format,
                SDL_TEXTUREACCESS_STREAMING,
                inputWidth,
                inputHeight
            );

            running = true;
            thread = CreateThread(nullptr, 0, startThread, this, 0, nullptr);
        }

        ~Screen() {
            running = false;
            WaitForSingleObject(thread, INFINITE);
            CloseHandle(thread);

            SDL_FreeSurface(surface);
            SDL_DestroyRenderer(renderer);
            SDL_DestroyWindow(window);
            SDL_Quit();
        }

        bool isRunning() const {
            return running;
        }
};

#endif

【问题讨论】:

  • 作为第一步,你为什么不暂停程序执行并检查你的线程挂在哪里?在 Visual Studio 和许多其他调试器中,这是很有可能的,如果你可以在你的 MinGW 环境中这样做,你应该这样做。如果您处于可重现的死锁状态,这是首先要尝试的方法。

标签: windows multithreading c++11 sdl-2


【解决方案1】:

我没有在多线程环境中使用 SDL API 的经验,但这不是什么大问题,稍后您会看到。我检查了您的代码,我认为您至少应该更改一件事。

  1. 一般来说,对于 GUI 系统(部分 SDL 也是一个 gui 系统),您应该始终只从主线程访问 gui,并期望 gui 事件来自主线程。大多数 GUI API 都是单线程的,如果这也适用于 SDL,我不会感到惊讶。请注意,许多 gui 系统默认在您的进程的主线程上运行,您不能选择自己的线程。不要在工作线程上运行 Screen 类的代码,而是在主线程上运行它并从主线程调用每个 SDL API。
  2. 如果您正在编写游戏或类似软件,则(首先)将其编写为单线程。引擎的子系统(物理模拟、这个和那个系统、游戏逻辑、渲染)应该在你的主线程(从你的主循环)上一个接一个地连续执行。如果您想在“另一个维度”中使用多线程:将一些子系统或较小的工作单元(如合并排序)转换为多线程,例如物理系统任务通常可以拆分为几个小任务所以当主线程更新物理系统时,物理系统可以烧掉你所有的核心......

在主线程上执行大部分任务还有另一个优势:它使您的代码更容易移植到任何平台。或者,如果您编写代码以便它可以在单线程模式下执行,那么它可以在许多情况下使调试更容易,然后您还有一个“参考构建”来将多线程构建与性能进行比较。

【讨论】:

  • 非常感谢!我对其进行了更改,使Screen 永远不会产生线程,使run() 可公开访问,在RTSC 中给run() 一个包装器调用,并主调用该循环。现在完美运行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-09
  • 2019-05-08
  • 1970-01-01
  • 1970-01-01
  • 2010-10-29
  • 1970-01-01
相关资源
最近更新 更多