C++ std::async run on main thread

后端 未结 2 1835
囚心锁ツ
囚心锁ツ 2021-02-01 23:05

IS there a way of running a function back on the main thread ?

So if I called a function via Async that downloaded a file and then parsed the data. It would then call a

相关标签:
2条回答
  • 2021-02-01 23:48

    Are you looking for std::launch::deferred ? Passing this parameter to std::async makes the task executed on the calling thread when the get() function is called for the first time.

    0 讨论(0)
  • 2021-02-01 23:52

    I have been reading C++ Concurrency in Action and chapter four (AKA "The Chapter I Just Finished") describes a solution.

    The Short Version

    Have a shared std::deque<std::packaged_task<void()>> (or a similar sort of message/task queue). Your std::async-launched functions can push tasks to the queue, and your GUI thread can process them during its loop.

    There Isn't Really a Long Version, but Here Is an Example

    Shared Data

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

    The std::async Function

    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();
    }
    

    The GUI Thread

    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));
        }
    }
    

    Notes:

    1. I am (always) learning, so there is a decent chance that my code is not great. The concept is at least sound though.

    2. The destructor of the return value from std::async (a std::future<>) will block until the operation launched with std::async completes (see std::async ), so waiting on the result of a task (as I do in my example) in one_off might not be a brilliant idea.

    3. You may want to (I would, at least) create your own threadsafe MessageQueue type to improve code readability/maintainability/blah blah blah.

    4. I swear there was one more thing I wanted to point out, but it escapes me right now.

    Full Example

    #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 Output

    $ ./messages
    main thread: 140299226687296
    gui thread: 140299210073856
    140299210073856
    140299210073856
    140299210073856
    140299210073856
    140299210073856
    
    0 讨论(0)
提交回复
热议问题