《深入应用C++11》笔记-异步线程操作std::async、std::future、std::promise、std::packaged_task

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/WizardtoH/article/details/81635669

上一篇:《深入应用C++11》笔记-互斥量std::mutex、锁std::lock_guard

std::async和std::future

std::async()是一个接受回调函数或函数对象作为参数的函数模板,并可以异步执行它们。通过这个异步接口可以很方便的获取线程函数的执行结果,std::async会自动创建一个线程去调用线程函数,它返回一个std::future。

std::future中存储了线程函数返回的结果,当我们需要线程函数的结果时,直接从future中获取。std::async首先解耦了线程的创建和执行,使得我们可以在需要的时候获取异步操作的结果;其次它还提供了线程的创建策略(比如可以通过延迟加载的方式去创建线程),可以以多种方式去创建线程。

template<class Fn, class... Args>
future<typename result_of<Fn(Args...)>::type> async(launch policy, Fn&& fn, Args&&...args);

std::async中的第一个参数是启动策略,它控制std::async的异步行为,共有三类:

  • std::launch::async:立即开始执行线程
  • std::launch::deferred:调用get()函数时才开始执行线程
  • std::launch::async | std::launch::deferred:默认行为。有了这个启动策略,它可以异步运行或不运行,这取决于系统的负载,但我们无法控制它。
#include <iostream>
#include <future>
#include <chrono>

int func()
{
    std::cout << "run async" << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    return 1;
}

int main()
{
    auto handle = std::async(std::launch::async, func);
    // 用deferred输出结果顺序将会不同
    //auto handle = std::async(std::launch::deferred, func);
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    std::cout << "do something" << std::endl;
    std::cout << handle.get() << std::endl;

    return 0;
}

输出结果:
run async
do something
1

可以看到,std::async异步执行了func函数,并且能够通过handle获取到func函数的返回值。

返回值handle实际上是std::future模板类对象,可使用std::future::get获取结果,如果调用过程中,任务尚未完成,则主线程阻塞至任务完成。也可使用std::future::wait_for等待结果返回,wait_for可设置超时时间,如果在超时时间之内任务完成,则返回std::future_status::ready状态;如果在超时时间之内任务尚未完成,则返回std::future_status::timeout状态。更加详细的用法参考:https://blog.csdn.net/watson2016/article/details/52860797

std::promise

promise 对象可以保存某一类型 T 的值,该值可被 future 对象读取。可以通过 get_future 来获取与该 promise 对象相关联的 future 对象,调用该函数之后,两个对象共享相同的共享状态(shared state)。

#include <iostream>
#include <functional>
#include <thread>
#include <future>

void func(std::promise<int>& pro){
    std::this_thread::sleep_for(std::chrono::milliseconds(2000));
    pro.set_value(10);                         // 设置值
}

int main()
{
    std::promise<int> pro;
    std::thread t(func, std::ref(pro));

    std::future<int> fut = pro.get_future();
    std::cout << fut.get() << std::endl;      // 10,调用get函数等待pro设置值
    t.join();

    return 0;
}

另外std::promise还提供了set_exception函数,让future对象的get函数抛出异常;提供set_value_at_thread_exit用于在线程退出时设置值,也就是说直到线程退出get阻塞才会结束,并且std::promise对象不能在线程结束前被释放。

std::packaged_task

std::packaged_task 和 std::promise很类似,不过他包装一个可调用的对象而不是类型对象,并且允许异步获取该可调用对象产生的结果。

int func(int from, int to) {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return from - to;
}

int main()
{
    std::packaged_task<int(int, int)> task(func);
    std::future<int> ret = task.get_future();

    std::thread th(std::move(task), 10, 5);
    std::cout << ret.get() << std::endl;          // 5,调用get函数等待func的返回值

    th.join();

    return 0;
}

valid函数,判断是否有效,对于由默认构造函数生成的 packaged_task 对象,该函数返回 false:

std::future<int> launcher(std::packaged_task<int(int)>& tsk, int arg)
{
    if (tsk.valid()) {                         // 无效返回false
        std::future<int> ret = tsk.get_future();
        std::thread(std::move(tsk), arg).detach();
        return ret;
    }
    else return std::future<int>();
}

int main()
{
    std::packaged_task<int(int)> tsk;         // 没有给packaged_task初始化

    std::future<int> fut = launcher(tsk, 25);

    if (fut.valid())                          // 无效返回false
    {
        std::cout << << fut.get() << std::endl;
    }

    return 0;
}

std::packaged_task还提供了reset函数,用于重置std::packaged_task状态,让他能够再次调用;提供make_ready_at_thread_exit函数,直到线程退出get阻塞才会结束,而不是可调用对象结束时。

下一篇:《深入应用C++11》笔记-时间相关工具chrono,duration、time_point、clock

猜你喜欢

转载自blog.csdn.net/WizardtoH/article/details/81635669
今日推荐