17

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

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

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

4

2 回答 2

17

我一直在阅读C++ Concurrency in Action和第四章(又名“我刚刚完成的章节”)描述了一个解决方案。

短版

有一个共享的std::deque<std::packaged_task<void()>>(或类似的消息/任务队列)。您的std::async-launched 函数可以将任务推送到队列中,并且您的 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(a )的返回值的析构函数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();
}

数据输出

$ ./messages
main thread: 140299226687296
gui thread: 140299210073856
140299210073856
140299210073856
140299210073856
140299210073856
140299210073856
于 2013-06-28T01:47:04.387 回答
0

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

于 2013-06-27T23:42:40.423 回答