C++std::在主线程上异步运行

C++ std::async run on main thread

本文关键字:异步 运行 C++std 线程      更新时间:2023-10-16

有没有办法在主线程上运行函数?

因此,如果我通过Async调用一个函数,该函数下载了一个文件,然后解析了数据。然后它会调用一个回调函数,该函数将在我的主UI线程上运行并更新UI?

我知道线程在默认的C++实现中是相等的,所以我必须创建一个指向主线程的共享指针。我该如何做到这一点,并将异步函数不仅传递到主线程的共享指针,还传递到我想在其上运行的函数的指针,然后在该主线程上运行它?

我一直在阅读C++操作中的并发,第四章(又名"第一章刚刚完成")描述了一个解决方案。

简短版本

有一个共享的std::deque<std::packaged_task<void()>>(或类似类型的消息/任务队列)。std::async启动的函数可以将任务推送到队列,GUI线程可以在循环过程中处理它们。

虽然没有很长的版本,但这里有一个例子

共享数据

std::deque<std::packaged_task<void()>> tasks;
std::mutex tasks_mutex;
std::atomic<bool> gui_running;

std::async函数

void one_off()
{
    std::packaged_task<void()> task(FUNCTION TO RUN ON GUI THREAD); //!!
    std::future<void> result = task.get_future();
    {
        std::lock_guard<std::mutex> lock(tasks_mutex);
        tasks.push_back(std::move(task));
    }
    // wait on result
    result.get();
}

GUI线程

void gui_thread()
{
    while (gui_running) {
        // process messages
        {
            std::unique_lock<std::mutex> lock(tasks_mutex);
            while (!tasks.empty()) {
                auto task(std::move(tasks.front()));
                tasks.pop_front();
                // unlock during the task
                lock.unlock();
                task();
                lock.lock();
            }
        }
        // "do gui work"
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}

注意事项:

  1. 我(一直)在学习,所以我的代码很有可能不太好。不过,这个概念至少是合理的。

  2. std::async(一个std::future<>)返回值的析构函数将阻塞,直到用std::async启动的操作完成(请参见std::async),因此在one_off中等待任务的结果(就像我在示例中所做的那样)可能不是一个好主意。

  3. 您可能想要(至少我会)创建自己的线程安全MessageQueue类型,以提高代码的可读性/可维护性等等。

  4. 我发誓还有一件事我想指出,但我现在想不出来了。

完整示例

#include <atomic>
#include <chrono>
#include <deque>
#include <iostream>
#include <mutex>
#include <future>
#include <thread>

// shared stuff:
std::deque<std::packaged_task<void()>> tasks;
std::mutex tasks_mutex;
std::atomic<bool> gui_running;

void message()
{
   std::cout << std::this_thread::get_id() << std::endl;
}

void one_off()
{
    std::packaged_task<void()> task(message);
    std::future<void> result = task.get_future();
    {
        std::lock_guard<std::mutex> lock(tasks_mutex);
        tasks.push_back(std::move(task));
    }
    // wait on result
    result.get();
}

void gui_thread()
{
    std::cout << "gui thread: "; message();
    while (gui_running) {
        // process messages
        {
            std::unique_lock<std::mutex> lock(tasks_mutex);
            while (!tasks.empty()) {
                auto task(std::move(tasks.front()));
                tasks.pop_front();
                // unlock during the task
                lock.unlock();
                task();
                lock.lock();
            }
        }
        // "do gui work"
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}

int main()
{
    gui_running = true;
    std::cout << "main thread: "; message();
    std::thread gt(gui_thread);
    for (unsigned i = 0; i < 5; ++i) {
        // note:
        // these will be launched sequentially because result's
        // destructor will block until one_off completes
        auto result = std::async(std::launch::async, one_off);
        // maybe do something with result if it is not void
    }
    // the for loop will not complete until all the tasks have been
    // processed by gui_thread
    // ...
    // cleanup
    gui_running = false;
    gt.join();
}

Dat输出

$ ./messages
main thread: 140299226687296
gui thread: 140299210073856
140299210073856
140299210073856
140299210073856
140299210073856
140299210073856

你在找std::launch::deferred吗?当第一次调用get()函数时,将此参数传递给std::async会使任务在调用线程上执行。