What is the difference between packaged_task and async

后端 未结 3 1405
悲&欢浪女
悲&欢浪女 2020-11-30 17:02

While working with the threaded model of C++11, I noticed that

std::packaged_task task([](int a, int b) { return a + b; });
auto f = task         


        
相关标签:
3条回答
  • 2020-11-30 17:09

    Actually the example you just gave shows the differences if you use a rather long function, such as

    //! sleeps for one second and returns 1
    auto sleep = [](){
        std::this_thread::sleep_for(std::chrono::seconds(1));
        return 1;
    };
    

    Packaged task

    A packaged_task won't start on it's own, you have to invoke it:

    std::packaged_task<int()> task(sleep);
    
    auto f = task.get_future();
    task(); // invoke the function
    
    // You have to wait until task returns. Since task calls sleep
    // you will have to wait at least 1 second.
    std::cout << "You can see this after 1 second\n";
    
    // However, f.get() will be available, since task has already finished.
    std::cout << f.get() << std::endl;
    

    std::async

    On the other hand, std::async with launch::async will try to run the task in a different thread:

    auto f = std::async(std::launch::async, sleep);
    std::cout << "You can see this immediately!\n";
    
    // However, the value of the future will be available after sleep has finished
    // so f.get() can block up to 1 second.
    std::cout << f.get() << "This will be shown after a second!\n";
    

    Drawback

    But before you try to use async for everything, keep in mind that the returned future has a special shared state, which demands that future::~future blocks:

    std::async(do_work1); // ~future blocks
    std::async(do_work2); // ~future blocks
    
    /* output: (assuming that do_work* log their progress)
        do_work1() started;
        do_work1() stopped;
        do_work2() started;
        do_work2() stopped;
    */
    

    So if you want real asynchronous you need to keep the returned future, or if you don't care for the result if the circumstances change:

    {
        auto pizza = std::async(get_pizza);
        /* ... */
        if(need_to_go)
            return;          // ~future will block
        else
           eat(pizza.get());
    }   
    

    For more information on this, see Herb Sutter's article async and ~future, which describes the problem, and Scott Meyer's std::futures from std::async aren't special, which describes the insights. Also do note that this behavior was specified in C++14 and up, but also commonly implemented in C++11.

    Further differences

    By using std::async you cannot run your task on a specific thread anymore, where std::packaged_task can be moved to other threads.

    std::packaged_task<int(int,int)> task(...);
    auto f = task.get_future();
    std::thread myThread(std::move(task),2,3);
    
    std::cout << f.get() << "\n";
    

    Also, a packaged_task needs to be invoked before you call f.get(), otherwise you program will freeze as the future will never become ready:

    std::packaged_task<int(int,int)> task(...);
    auto f = task.get_future();
    std::cout << f.get() << "\n"; // oops!
    task(2,3);
    

    TL;DR

    Use std::async if you want some things done and don't really care when they're done, and std::packaged_task if you want to wrap up things in order to move them to other threads or call them later. Or, to quote Christian:

    In the end a std::packaged_task is just a lower level feature for implementing std::async (which is why it can do more than std::async if used together with other lower level stuff, like std::thread). Simply spoken a std::packaged_task is a std::function linked to a std::future and std::async wraps and calls a std::packaged_task (possibly in a different thread).

    0 讨论(0)
  • 2020-11-30 17:12

    Packaged Task vs async

    p> Packaged task holds a task [function or function object] and future/promise pair. When the task executes a return statement, it causes set_value(..) on the packaged_task's promise.

    a> Given Future, promise and package task we can create simple tasks without worrying too much about threads [thread is just something we give to run a task].

    However we need to consider how many threads to use or whether a task is best run on the current thread or on another etc.Such descisions can be handled by a thread launcher called async(), that decides whether to create a new a thread or recycle an old one or simply run the task on the current thread. It returns a future .

    0 讨论(0)
  • 2020-11-30 17:27

    "The class template std::packaged_task wraps any callable target (function, lambda expression, bind expression, or another function object) so that it can be invoked asynchronously. Its return value or exception thrown is stored in a shared state which can be accessed through std::future objects."

    "The template function async runs the function f asynchronously (potentially in a separate thread) and returns a std::future that will eventually hold the result of that function call."

    0 讨论(0)
提交回复
热议问题