C++中将数据从事件线程传递到持续运行的线程的最有效方式(20)

问题描述 投票:0回答:0

我正在进行一个研究项目,本质上是一个以 30 fps 显示一系列图像的 Windows 窗口。我最初一直在使用 GLFW。但是,API 的问题(尽管非常方便)是当用户通过单击并拖动窗口标题栏来移动窗口时,或者当用户将鼠标按住窗口的右下角时(调整手柄)不移动鼠标,窗口的内容不会刷新(由于 Windows 的模态性质,如果我做对了,它不会释放对这种特殊情况的关注)。

所以我需要创建两个线程:一个用于处理循环(处理图像并将它们每 1/30 秒显示到屏幕上)和一个处理事件(鼠标)。因为在这个项目中,我不能使用 GLFW 回调函数来捕获事件(我的约束),我需要使用本机 Windows API。但是为了简化代码,我将只是伪造事件的创建。这个想法是在主线程中捕获事件,将它们推送到队列中。然后负责处理和显示图像的线程获取事件队列,取消排队(并处理)事件,然后为图像做它的事情,等待 1/30 秒(减去做这些事情所花费的所有时间上面),继续前进。

下面的代码似乎有效。

  • 每次触发事件时,都会调用
    handle_event
    函数。它锁定互斥锁,并将事件添加到
    event_queue
    。当我们从函数返回时,互斥锁被解锁。
  • 处理线程,获取锁,取消事件队列等(在下面的示例中,我只是从队列中弹出事件)。
#include <iostream>

#include <queue>
#include <chrono>
#include <mutex>
#include <condition_variable>

using namespace std::chrono_literals;

std::mutex m;

struct Event
{
    int msg;
};

std::queue<Event> event_queue;

void handle_event()
{
    std::cerr << "new event " << std::endl;
    std::lock_guard lock(m);
    event_queue.push({0});
}

using one_cycle = std::chrono::duration<std::int64_t, std::ratio<1, 30>>;
auto time_point = std::chrono::steady_clock::now() + one_cycle{0};

void constantly_running_proc()
{
    while (1) {
        {
            std::lock_guard lock(m);
            std::cerr << "event queue size: " << event_queue.size() << std::endl;
            while (!event_queue.empty()) {
                Event e = event_queue.back();
                event_queue.pop();
                //do_something_with_event(e);
            }
        }
        
        // process the events
        // ...

        // process the current frame
        // ...

        time_point += one_cycle{1};
        std::this_thread::sleep_until(time_point);
        
        //display_frame();
    }
}

int main() 
{
    std::thread t(constantly_running_proc);
    while (1) {
        handle_event();
        std::this_thread::sleep_for(100ms * rand() / (float)RAND_MAX);
    }

    return 0;
}
  1. 首先,这对您来说是否正确?你看到这段代码有什么问题吗?

  2. 虽然它在实践中似乎有效,但

    constantly_running_proc
    总是获取互斥锁以线程安全的方式访问事件队列,即使队列为空也是一种浪费。这可能没什么大不了的。但是因为对于这个练习,我试图尽可能地提高语言允许的效率(它需要在 1/30 秒内说出来,并且图像的处理可能很繁重 - 节省周期可能是一件好事在这种情况下。我也在学习。我一直在思考和尝试使用
    condition_variable
    。我试过这个:

std::condition_variable cv;

void handle_event()
{
    std::cerr << "new event " << std::endl;
    std::lock_guard lock(m);
    event_queue.push({0});
    cv.notify_one();
}

void constantly_running_proc2()
{
    while (1) {
        
        // process the current frame
        // ...

        time_point += one_cycle{1};
        std::unique_lock lock(m);
        if (cv.wait_until(lock, time_point, []() { return !event_queue.empty(); })) {
            std::cerr << "event queue size: " << event_queue.size() << std::endl;
            while (!event_queue.empty()) {
                event_queue.pop();
            }
            std::this_thread::sleep_until(time_point);
        }
        else {
            // timeout - nothing to do
        }
        
        //display_frame();
    }
}

我想这也行,但是在

std::this_thread::sleep_until
块内添加另一个
if (cv.wait_until)
似乎有点矫枉过正。

非常感谢向 C++ 专家/大师学习这个问题(我希望将来会有很多人愿意找到答案)。

c++ multithreading event-loop condition-variable
© www.soinside.com 2019 - 2024. All rights reserved.