C++ std::async run on main thread C++ std::async run on main thread multithreading multithreading

C++ std::async run on main thread


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

$ ./messagesmain thread: 140299226687296gui thread: 140299210073856140299210073856140299210073856140299210073856140299210073856140299210073856


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.